roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} 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     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @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)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @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,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} summaryFooterShow (true|false) generate tfoot for summary, default false
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     summaryFooterShow : false,
9346     headerShow : true,
9347     enableColumnResize: true,
9348     disableAutoSize: false,
9349   
9350     rowSelection : false,
9351     cellSelection : false,
9352     layout : false,
9353
9354     minColumnWidth : 50,
9355     
9356     // Roo.Element - the tbody
9357     bodyEl: false,  // <tbody> Roo.Element - thead element    
9358     headEl: false,  // <thead> Roo.Element - thead element
9359     resizeProxy : false, // proxy element for dragging?
9360
9361
9362     
9363     container: false, // used by gridpanel...
9364     
9365     lazyLoad : false,
9366     
9367     CSS : Roo.util.CSS,
9368     
9369     auto_hide_footer : false,
9370     
9371     view: false, // actually points to this..
9372     
9373     getAutoCreate : function()
9374     {
9375         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376         
9377         cfg = {
9378             tag: 'table',
9379             cls : 'table', 
9380             cn : []
9381         };
9382         // this get's auto added by panel.Grid
9383         if (this.scrollBody) {
9384             cfg.cls += ' table-body-fixed';
9385         }    
9386         if (this.striped) {
9387             cfg.cls += ' table-striped';
9388         }
9389         
9390         if (this.hover) {
9391             cfg.cls += ' table-hover';
9392         }
9393         if (this.bordered) {
9394             cfg.cls += ' table-bordered';
9395         }
9396         if (this.condensed) {
9397             cfg.cls += ' table-condensed';
9398         }
9399         
9400         if (this.responsive) {
9401             cfg.cls += ' table-responsive';
9402         }
9403         
9404         if (this.cls) {
9405             cfg.cls+=  ' ' +this.cls;
9406         }
9407         
9408         
9409         
9410         if (this.layout) {
9411             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412         }
9413         
9414         if(this.store || this.cm){
9415             if(this.headerShow){
9416                 cfg.cn.push(this.renderHeader());
9417             }
9418             
9419             cfg.cn.push(this.renderBody());
9420             
9421             if(this.footerShow || this.summaryFooterShow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             // where does this come from?
9426             //cfg.cls+=  ' TableGrid';
9427         }
9428         
9429         return { cn : [ cfg ] };
9430     },
9431     
9432     initEvents : function()
9433     {   
9434         if(!this.store || !this.cm){
9435             return;
9436         }
9437         if (this.selModel) {
9438             this.selModel.initEvents();
9439         }
9440         
9441         
9442         //Roo.log('initEvents with ds!!!!');
9443         
9444         this.bodyEl = this.el.select('tbody', true).first();
9445         this.headEl = this.el.select('thead', true).first();
9446         this.mainFoot = this.el.select('tfoot', true).first();
9447         
9448         
9449         
9450         
9451         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9452             e.on('click', this.sort, this);
9453         }, this);
9454         
9455         
9456         // why is this done????? = it breaks dialogs??
9457         //this.parent().el.setStyle('position', 'relative');
9458         
9459         
9460         if (this.footer) {
9461             this.footer.parentId = this.id;
9462             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9463             
9464             if(this.lazyLoad){
9465                 this.el.select('tfoot tr td').first().addClass('hide');
9466             }
9467         } 
9468         
9469         if(this.loadMask) {
9470             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9471         }
9472         
9473         this.store.on('load', this.onLoad, this);
9474         this.store.on('beforeload', this.onBeforeLoad, this);
9475         this.store.on('update', this.onUpdate, this);
9476         this.store.on('add', this.onAdd, this);
9477         this.store.on("clear", this.clear, this);
9478         
9479         this.el.on("contextmenu", this.onContextMenu, this);
9480         
9481         
9482         this.cm.on("headerchange", this.onHeaderChange, this);
9483         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9484
9485  //?? does bodyEl get replaced on render?
9486         this.bodyEl.on("click", this.onClick, this);
9487         this.bodyEl.on("dblclick", this.onDblClick, this);        
9488         this.bodyEl.on('scroll', this.onBodyScroll, this);
9489
9490         // guessing mainbody will work - this relays usually caught by selmodel at present.
9491         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9492   
9493   
9494         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9495         
9496   
9497         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9498             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9499         }
9500         
9501         this.initCSS();
9502     },
9503     // Compatibility with grid - we implement all the view features at present.
9504     getView : function()
9505     {
9506         return this;
9507     },
9508     
9509     initCSS : function()
9510     {
9511         if(this.disableAutoSize) {
9512             return;
9513         }
9514         
9515         var cm = this.cm, styles = [];
9516         this.CSS.removeStyleSheet(this.id + '-cssrules');
9517         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9518         // we can honour xs/sm/md/xl  as widths...
9519         // we first have to decide what widht we are currently at...
9520         var sz = Roo.getGridSize();
9521         
9522         var total = 0;
9523         var last = -1;
9524         var cols = []; // visable cols.
9525         var total_abs = 0;
9526         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9527             var w = cm.getColumnWidth(i, false);
9528             if(cm.isHidden(i)){
9529                 cols.push( { rel : false, abs : 0 });
9530                 continue;
9531             }
9532             if (w !== false) {
9533                 cols.push( { rel : false, abs : w });
9534                 total_abs += w;
9535                 last = i; // not really..
9536                 continue;
9537             }
9538             var w = cm.getColumnWidth(i, sz);
9539             if (w > 0) {
9540                 last = i
9541             }
9542             total += w;
9543             cols.push( { rel : w, abs : false });
9544         }
9545         
9546         var avail = this.bodyEl.dom.clientWidth - total_abs;
9547         
9548         var unitWidth = Math.floor(avail / total);
9549         var rem = avail - (unitWidth * total);
9550         
9551         var hidden, width, pos = 0 , splithide , left;
9552         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9553             
9554             hidden = 'display:none;';
9555             left = '';
9556             width  = 'width:0px;';
9557             splithide = '';
9558             if(!cm.isHidden(i)){
9559                 hidden = '';
9560                 
9561                 
9562                 // we can honour xs/sm/md/xl ?
9563                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9564                 if (w===0) {
9565                     hidden = 'display:none;';
9566                 }
9567                 // width should return a small number...
9568                 if (i == last) {
9569                     w+=rem; // add the remaining with..
9570                 }
9571                 pos += w;
9572                 left = "left:" + (pos -4) + "px;";
9573                 width = "width:" + w+ "px;";
9574                 
9575             }
9576             if (this.responsive) {
9577                 width = '';
9578                 left = '';
9579                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9580                 splithide = 'display: none;';
9581             }
9582             
9583             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9584             if (this.headEl) {
9585                 if (i == last) {
9586                     splithide = 'display:none;';
9587                 }
9588                 
9589                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9590                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9591                             // this is the popover version..
9592                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9593                 );
9594             }
9595             
9596         }
9597         //Roo.log(styles.join(''));
9598         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599         
9600     },
9601     
9602     
9603     
9604     onContextMenu : function(e, t)
9605     {
9606         this.processEvent("contextmenu", e);
9607     },
9608     
9609     processEvent : function(name, e)
9610     {
9611         if (name != 'touchstart' ) {
9612             this.fireEvent(name, e);    
9613         }
9614         
9615         var t = e.getTarget();
9616         
9617         var cell = Roo.get(t);
9618         
9619         if(!cell){
9620             return;
9621         }
9622         
9623         if(cell.findParent('tfoot', false, true)){
9624             return;
9625         }
9626         
9627         if(cell.findParent('thead', false, true)){
9628             
9629             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9630                 cell = Roo.get(t).findParent('th', false, true);
9631                 if (!cell) {
9632                     Roo.log("failed to find th in thead?");
9633                     Roo.log(e.getTarget());
9634                     return;
9635                 }
9636             }
9637             
9638             var cellIndex = cell.dom.cellIndex;
9639             
9640             var ename = name == 'touchstart' ? 'click' : name;
9641             this.fireEvent("header" + ename, this, cellIndex, e);
9642             
9643             return;
9644         }
9645         
9646         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9647             cell = Roo.get(t).findParent('td', false, true);
9648             if (!cell) {
9649                 Roo.log("failed to find th in tbody?");
9650                 Roo.log(e.getTarget());
9651                 return;
9652             }
9653         }
9654         
9655         var row = cell.findParent('tr', false, true);
9656         var cellIndex = cell.dom.cellIndex;
9657         var rowIndex = row.dom.rowIndex - 1;
9658         
9659         if(row !== false){
9660             
9661             this.fireEvent("row" + name, this, rowIndex, e);
9662             
9663             if(cell !== false){
9664             
9665                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666             }
9667         }
9668         
9669     },
9670     
9671     onMouseover : function(e, el)
9672     {
9673         var cell = Roo.get(el);
9674         
9675         if(!cell){
9676             return;
9677         }
9678         
9679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9680             cell = cell.findParent('td', false, true);
9681         }
9682         
9683         var row = cell.findParent('tr', false, true);
9684         var cellIndex = cell.dom.cellIndex;
9685         var rowIndex = row.dom.rowIndex - 1; // start from 0
9686         
9687         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9688         
9689     },
9690     
9691     onMouseout : function(e, el)
9692     {
9693         var cell = Roo.get(el);
9694         
9695         if(!cell){
9696             return;
9697         }
9698         
9699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9700             cell = cell.findParent('td', false, true);
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         var cellIndex = cell.dom.cellIndex;
9705         var rowIndex = row.dom.rowIndex - 1; // start from 0
9706         
9707         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9708         
9709     },
9710     
9711     onClick : function(e, el)
9712     {
9713         var cell = Roo.get(el);
9714         
9715         if(!cell || (!this.cellSelection && !this.rowSelection)){
9716             return;
9717         }
9718         
9719         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9720             cell = cell.findParent('td', false, true);
9721         }
9722         
9723         if(!cell || typeof(cell) == 'undefined'){
9724             return;
9725         }
9726         
9727         var row = cell.findParent('tr', false, true);
9728         
9729         if(!row || typeof(row) == 'undefined'){
9730             return;
9731         }
9732         
9733         var cellIndex = cell.dom.cellIndex;
9734         var rowIndex = this.getRowIndex(row);
9735         
9736         // why??? - should these not be based on SelectionModel?
9737         //if(this.cellSelection){
9738             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9739         //}
9740         
9741         //if(this.rowSelection){
9742             this.fireEvent('rowclick', this, row, rowIndex, e);
9743         //}
9744          
9745     },
9746         
9747     onDblClick : function(e,el)
9748     {
9749         var cell = Roo.get(el);
9750         
9751         if(!cell || (!this.cellSelection && !this.rowSelection)){
9752             return;
9753         }
9754         
9755         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9756             cell = cell.findParent('td', false, true);
9757         }
9758         
9759         if(!cell || typeof(cell) == 'undefined'){
9760             return;
9761         }
9762         
9763         var row = cell.findParent('tr', false, true);
9764         
9765         if(!row || typeof(row) == 'undefined'){
9766             return;
9767         }
9768         
9769         var cellIndex = cell.dom.cellIndex;
9770         var rowIndex = this.getRowIndex(row);
9771         
9772         if(this.cellSelection){
9773             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9774         }
9775         
9776         if(this.rowSelection){
9777             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9778         }
9779     },
9780     findRowIndex : function(el)
9781     {
9782         var cell = Roo.get(el);
9783         if(!cell) {
9784             return false;
9785         }
9786         var row = cell.findParent('tr', false, true);
9787         
9788         if(!row || typeof(row) == 'undefined'){
9789             return false;
9790         }
9791         return this.getRowIndex(row);
9792     },
9793     sort : function(e,el)
9794     {
9795         var col = Roo.get(el);
9796         
9797         if(!col.hasClass('sortable')){
9798             return;
9799         }
9800         
9801         var sort = col.attr('sort');
9802         var dir = 'ASC';
9803         
9804         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9805             dir = 'DESC';
9806         }
9807         
9808         this.store.sortInfo = {field : sort, direction : dir};
9809         
9810         if (this.footer) {
9811             Roo.log("calling footer first");
9812             this.footer.onClick('first');
9813         } else {
9814         
9815             this.store.load({ params : { start : 0 } });
9816         }
9817     },
9818     
9819     renderHeader : function()
9820     {
9821         var header = {
9822             tag: 'thead',
9823             cn : []
9824         };
9825         
9826         var cm = this.cm;
9827         this.totalWidth = 0;
9828         
9829         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9830             
9831             var config = cm.config[i];
9832             
9833             var c = {
9834                 tag: 'th',
9835                 cls : 'x-hcol-' + i,
9836                 style : '',
9837                 
9838                 html: cm.getColumnHeader(i)
9839             };
9840             
9841             var tooltip = cm.getColumnTooltip(i);
9842             if (tooltip) {
9843                 c.tooltip = tooltip;
9844             }
9845             
9846             
9847             var hh = '';
9848             
9849             if(typeof(config.sortable) != 'undefined' && config.sortable){
9850                 c.cls += ' sortable';
9851                 c.html = '<i class="fa"></i>' + c.html;
9852             }
9853             
9854             // could use BS4 hidden-..-down 
9855             
9856             if(typeof(config.lgHeader) != 'undefined'){
9857                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9858             }
9859             
9860             if(typeof(config.mdHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.smHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.xsHeader) != 'undefined'){
9869                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9870             }
9871             
9872             if(hh.length){
9873                 c.html = hh;
9874             }
9875             
9876             if(typeof(config.tooltip) != 'undefined'){
9877                 c.tooltip = config.tooltip;
9878             }
9879             
9880             if(typeof(config.colspan) != 'undefined'){
9881                 c.colspan = config.colspan;
9882             }
9883             
9884             // hidden is handled by CSS now
9885             
9886             if(typeof(config.dataIndex) != 'undefined'){
9887                 c.sort = config.dataIndex;
9888             }
9889             
9890            
9891             
9892             if(typeof(config.align) != 'undefined' && config.align.length){
9893                 c.style += ' text-align:' + config.align + ';';
9894             }
9895             
9896             /* width is done in CSS
9897              *if(typeof(config.width) != 'undefined'){
9898                 c.style += ' width:' + config.width + 'px;';
9899                 this.totalWidth += config.width;
9900             } else {
9901                 this.totalWidth += 100; // assume minimum of 100 per column?
9902             }
9903             */
9904             
9905             if(typeof(config.cls) != 'undefined'){
9906                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9907             }
9908             // this is the bit that doesnt reall work at all...
9909             
9910             if (this.responsive) {
9911                  
9912             
9913                 ['xs','sm','md','lg'].map(function(size){
9914                     
9915                     if(typeof(config[size]) == 'undefined'){
9916                         return;
9917                     }
9918                      
9919                     if (!config[size]) { // 0 = hidden
9920                         // BS 4 '0' is treated as hide that column and below.
9921                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9922                         return;
9923                     }
9924                     
9925                     c.cls += ' col-' + size + '-' + config[size] + (
9926                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9927                     );
9928                     
9929                     
9930                 });
9931             }
9932             // at the end?
9933             
9934             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9935             
9936             
9937             
9938             
9939             header.cn.push(c)
9940         }
9941         
9942         return header;
9943     },
9944     
9945     renderBody : function()
9946     {
9947         var body = {
9948             tag: 'tbody',
9949             cn : [
9950                 {
9951                     tag: 'tr',
9952                     cn : [
9953                         {
9954                             tag : 'td',
9955                             colspan :  this.cm.getColumnCount()
9956                         }
9957                     ]
9958                 }
9959             ]
9960         };
9961         
9962         return body;
9963     },
9964     
9965     renderFooter : function()
9966     {
9967         var footer = {
9968             tag: 'tfoot',
9969             cn : [
9970                 {
9971                     tag: 'tr',
9972                     cn : [
9973                         {
9974                             tag : 'td',
9975                             colspan :  this.cm.getColumnCount()
9976                         }
9977                     ]
9978                 }
9979             ]
9980         };
9981         
9982         return footer;
9983     },
9984     
9985     onLoad : function()
9986     {
9987 //        Roo.log('ds onload');
9988         this.clear();
9989         
9990         var _this = this;
9991         var cm = this.cm;
9992         var ds = this.store;
9993         
9994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9995             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9996             if (_this.store.sortInfo) {
9997                     
9998                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9999                     e.select('i', true).addClass(['fa-arrow-up']);
10000                 }
10001                 
10002                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10003                     e.select('i', true).addClass(['fa-arrow-down']);
10004                 }
10005             }
10006         });
10007         
10008         var tbody =  this.bodyEl;
10009               
10010         if(ds.getCount() > 0){
10011             ds.data.each(function(d,rowIndex){
10012                 var row =  this.renderRow(cm, ds, rowIndex);
10013                 
10014                 tbody.createChild(row);
10015                 
10016                 var _this = this;
10017                 
10018                 if(row.cellObjects.length){
10019                     Roo.each(row.cellObjects, function(r){
10020                         _this.renderCellObject(r);
10021                     })
10022                 }
10023                 
10024             }, this);
10025         } else if (this.empty_results.length) {
10026             this.el.mask(this.empty_results, 'no-spinner');
10027         }
10028         
10029         var tfoot = this.el.select('tfoot', true).first();
10030         
10031         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10032             
10033             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10034             
10035             var total = this.ds.getTotalCount();
10036             
10037             if(this.footer.pageSize < total){
10038                 this.mainFoot.show();
10039             }
10040         }
10041
10042         if(!this.footerShow && this.summaryFooterShow) {
10043
10044             var tr = {
10045                 tag : 'tr',
10046                 cn : []
10047             };
10048
10049             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10050         
10051                 var value = cm.config[i].summaryFooter;
10052
10053                 Roo.log('value [' + i + '] : ' + value);
10054
10055                 var td = {
10056                     tag: 'td',
10057                     cls : ' x-fcol-' + i,
10058                     style: '',
10059                     html: cm.config[i].summaryFooter
10060                 };
10061
10062                 tr.cn.push(td);
10063                 
10064             }
10065             
10066             tfoot.createChild(row);
10067         }
10068         
10069         Roo.each(this.el.select('tbody td', true).elements, function(e){
10070             e.on('mouseover', _this.onMouseover, _this);
10071         });
10072         
10073         Roo.each(this.el.select('tbody td', true).elements, function(e){
10074             e.on('mouseout', _this.onMouseout, _this);
10075         });
10076         this.fireEvent('rowsrendered', this);
10077         
10078         this.autoSize();
10079         
10080         this.initCSS(); /// resize cols
10081
10082         
10083     },
10084     
10085     
10086     onUpdate : function(ds,record)
10087     {
10088         this.refreshRow(record);
10089         this.autoSize();
10090     },
10091     
10092     onRemove : function(ds, record, index, isUpdate){
10093         if(isUpdate !== true){
10094             this.fireEvent("beforerowremoved", this, index, record);
10095         }
10096         var bt = this.bodyEl.dom;
10097         
10098         var rows = this.el.select('tbody > tr', true).elements;
10099         
10100         if(typeof(rows[index]) != 'undefined'){
10101             bt.removeChild(rows[index].dom);
10102         }
10103         
10104 //        if(bt.rows[index]){
10105 //            bt.removeChild(bt.rows[index]);
10106 //        }
10107         
10108         if(isUpdate !== true){
10109             //this.stripeRows(index);
10110             //this.syncRowHeights(index, index);
10111             //this.layout();
10112             this.fireEvent("rowremoved", this, index, record);
10113         }
10114     },
10115     
10116     onAdd : function(ds, records, rowIndex)
10117     {
10118         //Roo.log('on Add called');
10119         // - note this does not handle multiple adding very well..
10120         var bt = this.bodyEl.dom;
10121         for (var i =0 ; i < records.length;i++) {
10122             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10123             //Roo.log(records[i]);
10124             //Roo.log(this.store.getAt(rowIndex+i));
10125             this.insertRow(this.store, rowIndex + i, false);
10126             return;
10127         }
10128         
10129     },
10130     
10131     
10132     refreshRow : function(record){
10133         var ds = this.store, index;
10134         if(typeof record == 'number'){
10135             index = record;
10136             record = ds.getAt(index);
10137         }else{
10138             index = ds.indexOf(record);
10139             if (index < 0) {
10140                 return; // should not happen - but seems to 
10141             }
10142         }
10143         this.insertRow(ds, index, true);
10144         this.autoSize();
10145         this.onRemove(ds, record, index+1, true);
10146         this.autoSize();
10147         //this.syncRowHeights(index, index);
10148         //this.layout();
10149         this.fireEvent("rowupdated", this, index, record);
10150     },
10151     // private - called by RowSelection
10152     onRowSelect : function(rowIndex){
10153         var row = this.getRowDom(rowIndex);
10154         row.addClass(['bg-info','info']);
10155     },
10156     // private - called by RowSelection
10157     onRowDeselect : function(rowIndex)
10158     {
10159         if (rowIndex < 0) {
10160             return;
10161         }
10162         var row = this.getRowDom(rowIndex);
10163         row.removeClass(['bg-info','info']);
10164     },
10165       /**
10166      * Focuses the specified row.
10167      * @param {Number} row The row index
10168      */
10169     focusRow : function(row)
10170     {
10171         //Roo.log('GridView.focusRow');
10172         var x = this.bodyEl.dom.scrollLeft;
10173         this.focusCell(row, 0, false);
10174         this.bodyEl.dom.scrollLeft = x;
10175
10176     },
10177      /**
10178      * Focuses the specified cell.
10179      * @param {Number} row The row index
10180      * @param {Number} col The column index
10181      * @param {Boolean} hscroll false to disable horizontal scrolling
10182      */
10183     focusCell : function(row, col, hscroll)
10184     {
10185         //Roo.log('GridView.focusCell');
10186         var el = this.ensureVisible(row, col, hscroll);
10187         // not sure what focusEL achives = it's a <a> pos relative 
10188         //this.focusEl.alignTo(el, "tl-tl");
10189         //if(Roo.isGecko){
10190         //    this.focusEl.focus();
10191         //}else{
10192         //    this.focusEl.focus.defer(1, this.focusEl);
10193         //}
10194     },
10195     
10196      /**
10197      * Scrolls the specified cell into view
10198      * @param {Number} row The row index
10199      * @param {Number} col The column index
10200      * @param {Boolean} hscroll false to disable horizontal scrolling
10201      */
10202     ensureVisible : function(row, col, hscroll)
10203     {
10204         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10205         //return null; //disable for testing.
10206         if(typeof row != "number"){
10207             row = row.rowIndex;
10208         }
10209         if(row < 0 && row >= this.ds.getCount()){
10210             return  null;
10211         }
10212         col = (col !== undefined ? col : 0);
10213         var cm = this.cm;
10214         while(cm.isHidden(col)){
10215             col++;
10216         }
10217
10218         var el = this.getCellDom(row, col);
10219         if(!el){
10220             return null;
10221         }
10222         var c = this.bodyEl.dom;
10223
10224         var ctop = parseInt(el.offsetTop, 10);
10225         var cleft = parseInt(el.offsetLeft, 10);
10226         var cbot = ctop + el.offsetHeight;
10227         var cright = cleft + el.offsetWidth;
10228
10229         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10230         var ch = 0; //?? header is not withing the area?
10231         var stop = parseInt(c.scrollTop, 10);
10232         var sleft = parseInt(c.scrollLeft, 10);
10233         var sbot = stop + ch;
10234         var sright = sleft + c.clientWidth;
10235         /*
10236         Roo.log('GridView.ensureVisible:' +
10237                 ' ctop:' + ctop +
10238                 ' c.clientHeight:' + c.clientHeight +
10239                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10240                 ' stop:' + stop +
10241                 ' cbot:' + cbot +
10242                 ' sbot:' + sbot +
10243                 ' ch:' + ch  
10244                 );
10245         */
10246         if(ctop < stop){
10247             c.scrollTop = ctop;
10248             //Roo.log("set scrolltop to ctop DISABLE?");
10249         }else if(cbot > sbot){
10250             //Roo.log("set scrolltop to cbot-ch");
10251             c.scrollTop = cbot-ch;
10252         }
10253
10254         if(hscroll !== false){
10255             if(cleft < sleft){
10256                 c.scrollLeft = cleft;
10257             }else if(cright > sright){
10258                 c.scrollLeft = cright-c.clientWidth;
10259             }
10260         }
10261
10262         return el;
10263     },
10264     
10265     
10266     insertRow : function(dm, rowIndex, isUpdate){
10267         
10268         if(!isUpdate){
10269             this.fireEvent("beforerowsinserted", this, rowIndex);
10270         }
10271             //var s = this.getScrollState();
10272         var row = this.renderRow(this.cm, this.store, rowIndex);
10273         // insert before rowIndex..
10274         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10275         
10276         var _this = this;
10277                 
10278         if(row.cellObjects.length){
10279             Roo.each(row.cellObjects, function(r){
10280                 _this.renderCellObject(r);
10281             })
10282         }
10283             
10284         if(!isUpdate){
10285             this.fireEvent("rowsinserted", this, rowIndex);
10286             //this.syncRowHeights(firstRow, lastRow);
10287             //this.stripeRows(firstRow);
10288             //this.layout();
10289         }
10290         
10291     },
10292     
10293     
10294     getRowDom : function(rowIndex)
10295     {
10296         var rows = this.el.select('tbody > tr', true).elements;
10297         
10298         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10299         
10300     },
10301     getCellDom : function(rowIndex, colIndex)
10302     {
10303         var row = this.getRowDom(rowIndex);
10304         if (row === false) {
10305             return false;
10306         }
10307         var cols = row.select('td', true).elements;
10308         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10309         
10310     },
10311     
10312     // returns the object tree for a tr..
10313   
10314     
10315     renderRow : function(cm, ds, rowIndex) 
10316     {
10317         var d = ds.getAt(rowIndex);
10318         
10319         var row = {
10320             tag : 'tr',
10321             cls : 'x-row-' + rowIndex,
10322             cn : []
10323         };
10324             
10325         var cellObjects = [];
10326         
10327         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10328             var config = cm.config[i];
10329             
10330             var renderer = cm.getRenderer(i);
10331             var value = '';
10332             var id = false;
10333             
10334             if(typeof(renderer) !== 'undefined'){
10335                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10336             }
10337             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10338             // and are rendered into the cells after the row is rendered - using the id for the element.
10339             
10340             if(typeof(value) === 'object'){
10341                 id = Roo.id();
10342                 cellObjects.push({
10343                     container : id,
10344                     cfg : value 
10345                 })
10346             }
10347             
10348             var rowcfg = {
10349                 record: d,
10350                 rowIndex : rowIndex,
10351                 colIndex : i,
10352                 rowClass : ''
10353             };
10354
10355             this.fireEvent('rowclass', this, rowcfg);
10356             
10357             var td = {
10358                 tag: 'td',
10359                 // this might end up displaying HTML?
10360                 // this is too messy... - better to only do it on columsn you know are going to be too long
10361                 //tooltip : (typeof(value) === 'object') ? '' : value,
10362                 cls : rowcfg.rowClass + ' x-col-' + i,
10363                 style: '',
10364                 html: (typeof(value) === 'object') ? '' : value
10365             };
10366             
10367             if (id) {
10368                 td.id = id;
10369             }
10370             
10371             if(typeof(config.colspan) != 'undefined'){
10372                 td.colspan = config.colspan;
10373             }
10374             
10375             
10376             
10377             if(typeof(config.align) != 'undefined' && config.align.length){
10378                 td.style += ' text-align:' + config.align + ';';
10379             }
10380             if(typeof(config.valign) != 'undefined' && config.valign.length){
10381                 td.style += ' vertical-align:' + config.valign + ';';
10382             }
10383             /*
10384             if(typeof(config.width) != 'undefined'){
10385                 td.style += ' width:' +  config.width + 'px;';
10386             }
10387             */
10388             
10389             if(typeof(config.cursor) != 'undefined'){
10390                 td.style += ' cursor:' +  config.cursor + ';';
10391             }
10392             
10393             if(typeof(config.cls) != 'undefined'){
10394                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10395             }
10396             if (this.responsive) {
10397                 ['xs','sm','md','lg'].map(function(size){
10398                     
10399                     if(typeof(config[size]) == 'undefined'){
10400                         return;
10401                     }
10402                     
10403                     
10404                       
10405                     if (!config[size]) { // 0 = hidden
10406                         // BS 4 '0' is treated as hide that column and below.
10407                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10408                         return;
10409                     }
10410                     
10411                     td.cls += ' col-' + size + '-' + config[size] + (
10412                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10413                     );
10414                      
10415     
10416                 });
10417             }
10418             row.cn.push(td);
10419            
10420         }
10421         
10422         row.cellObjects = cellObjects;
10423         
10424         return row;
10425           
10426     },
10427     
10428     
10429     
10430     onBeforeLoad : function()
10431     {
10432         this.el.unmask(); // if needed.
10433     },
10434      /**
10435      * Remove all rows
10436      */
10437     clear : function()
10438     {
10439         this.el.select('tbody', true).first().dom.innerHTML = '';
10440     },
10441     /**
10442      * Show or hide a row.
10443      * @param {Number} rowIndex to show or hide
10444      * @param {Boolean} state hide
10445      */
10446     setRowVisibility : function(rowIndex, state)
10447     {
10448         var bt = this.bodyEl.dom;
10449         
10450         var rows = this.el.select('tbody > tr', true).elements;
10451         
10452         if(typeof(rows[rowIndex]) == 'undefined'){
10453             return;
10454         }
10455         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10456         
10457     },
10458     
10459     
10460     getSelectionModel : function(){
10461         if(!this.selModel){
10462             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10463         }
10464         return this.selModel;
10465     },
10466     /*
10467      * Render the Roo.bootstrap object from renderder
10468      */
10469     renderCellObject : function(r)
10470     {
10471         var _this = this;
10472         
10473         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10474         
10475         var t = r.cfg.render(r.container);
10476         
10477         if(r.cfg.cn){
10478             Roo.each(r.cfg.cn, function(c){
10479                 var child = {
10480                     container: t.getChildContainer(),
10481                     cfg: c
10482                 };
10483                 _this.renderCellObject(child);
10484             })
10485         }
10486     },
10487     /**
10488      * get the Row Index from a dom element.
10489      * @param {Roo.Element} row The row to look for
10490      * @returns {Number} the row
10491      */
10492     getRowIndex : function(row)
10493     {
10494         var rowIndex = -1;
10495         
10496         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10497             if(el != row){
10498                 return;
10499             }
10500             
10501             rowIndex = index;
10502         });
10503         
10504         return rowIndex;
10505     },
10506     /**
10507      * get the header TH element for columnIndex
10508      * @param {Number} columnIndex
10509      * @returns {Roo.Element}
10510      */
10511     getHeaderIndex: function(colIndex)
10512     {
10513         var cols = this.headEl.select('th', true).elements;
10514         return cols[colIndex]; 
10515     },
10516     /**
10517      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10518      * @param {domElement} cell to look for
10519      * @returns {Number} the column
10520      */
10521     getCellIndex : function(cell)
10522     {
10523         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10524         if(id){
10525             return parseInt(id[1], 10);
10526         }
10527         return 0;
10528     },
10529      /**
10530      * Returns the grid's underlying element = used by panel.Grid
10531      * @return {Element} The element
10532      */
10533     getGridEl : function(){
10534         return this.el;
10535     },
10536      /**
10537      * Forces a resize - used by panel.Grid
10538      * @return {Element} The element
10539      */
10540     autoSize : function()
10541     {
10542         if(this.disableAutoSize) {
10543             return;
10544         }
10545         //var ctr = Roo.get(this.container.dom.parentElement);
10546         var ctr = Roo.get(this.el.dom);
10547         
10548         var thd = this.getGridEl().select('thead',true).first();
10549         var tbd = this.getGridEl().select('tbody', true).first();
10550         var tfd = this.getGridEl().select('tfoot', true).first();
10551         
10552         var cw = ctr.getWidth();
10553         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10554         
10555         if (tbd) {
10556             
10557             tbd.setWidth(ctr.getWidth());
10558             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10559             // this needs fixing for various usage - currently only hydra job advers I think..
10560             //tdb.setHeight(
10561             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10562             //); 
10563             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10564             cw -= barsize;
10565         }
10566         cw = Math.max(cw, this.totalWidth);
10567         this.getGridEl().select('tbody tr',true).setWidth(cw);
10568         this.initCSS();
10569         
10570         // resize 'expandable coloumn?
10571         
10572         return; // we doe not have a view in this design..
10573         
10574     },
10575     onBodyScroll: function()
10576     {
10577         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10578         if(this.headEl){
10579             this.headEl.setStyle({
10580                 'position' : 'relative',
10581                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10582             });
10583         }
10584         
10585         if(this.lazyLoad){
10586             
10587             var scrollHeight = this.bodyEl.dom.scrollHeight;
10588             
10589             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10590             
10591             var height = this.bodyEl.getHeight();
10592             
10593             if(scrollHeight - height == scrollTop) {
10594                 
10595                 var total = this.ds.getTotalCount();
10596                 
10597                 if(this.footer.cursor + this.footer.pageSize < total){
10598                     
10599                     this.footer.ds.load({
10600                         params : {
10601                             start : this.footer.cursor + this.footer.pageSize,
10602                             limit : this.footer.pageSize
10603                         },
10604                         add : true
10605                     });
10606                 }
10607             }
10608             
10609         }
10610     },
10611     onColumnSplitterMoved : function(i, diff)
10612     {
10613         this.userResized = true;
10614         
10615         var cm = this.colModel;
10616         
10617         var w = this.getHeaderIndex(i).getWidth() + diff;
10618         
10619         
10620         cm.setColumnWidth(i, w, true);
10621         this.initCSS();
10622         //var cid = cm.getColumnId(i); << not used in this version?
10623        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10624         
10625         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10626         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10627         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10628 */
10629         //this.updateSplitters();
10630         //this.layout(); << ??
10631         this.fireEvent("columnresize", i, w);
10632     },
10633     onHeaderChange : function()
10634     {
10635         var header = this.renderHeader();
10636         var table = this.el.select('table', true).first();
10637         
10638         this.headEl.remove();
10639         this.headEl = table.createChild(header, this.bodyEl, false);
10640         
10641         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10642             e.on('click', this.sort, this);
10643         }, this);
10644         
10645         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10646             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10647         }
10648         
10649     },
10650     
10651     onHiddenChange : function(colModel, colIndex, hidden)
10652     {
10653         /*
10654         this.cm.setHidden()
10655         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10656         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10657         
10658         this.CSS.updateRule(thSelector, "display", "");
10659         this.CSS.updateRule(tdSelector, "display", "");
10660         
10661         if(hidden){
10662             this.CSS.updateRule(thSelector, "display", "none");
10663             this.CSS.updateRule(tdSelector, "display", "none");
10664         }
10665         */
10666         // onload calls initCSS()
10667         this.onHeaderChange();
10668         this.onLoad();
10669     },
10670     
10671     setColumnWidth: function(col_index, width)
10672     {
10673         // width = "md-2 xs-2..."
10674         if(!this.colModel.config[col_index]) {
10675             return;
10676         }
10677         
10678         var w = width.split(" ");
10679         
10680         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10681         
10682         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10683         
10684         
10685         for(var j = 0; j < w.length; j++) {
10686             
10687             if(!w[j]) {
10688                 continue;
10689             }
10690             
10691             var size_cls = w[j].split("-");
10692             
10693             if(!Number.isInteger(size_cls[1] * 1)) {
10694                 continue;
10695             }
10696             
10697             if(!this.colModel.config[col_index][size_cls[0]]) {
10698                 continue;
10699             }
10700             
10701             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10702                 continue;
10703             }
10704             
10705             h_row[0].classList.replace(
10706                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10707                 "col-"+size_cls[0]+"-"+size_cls[1]
10708             );
10709             
10710             for(var i = 0; i < rows.length; i++) {
10711                 
10712                 var size_cls = w[j].split("-");
10713                 
10714                 if(!Number.isInteger(size_cls[1] * 1)) {
10715                     continue;
10716                 }
10717                 
10718                 if(!this.colModel.config[col_index][size_cls[0]]) {
10719                     continue;
10720                 }
10721                 
10722                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10723                     continue;
10724                 }
10725                 
10726                 rows[i].classList.replace(
10727                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10728                     "col-"+size_cls[0]+"-"+size_cls[1]
10729                 );
10730             }
10731             
10732             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10733         }
10734     }
10735 });
10736
10737 // currently only used to find the split on drag.. 
10738 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10739
10740 /**
10741  * @depricated
10742 */
10743 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10744 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10745 /*
10746  * - LGPL
10747  *
10748  * table cell
10749  * 
10750  */
10751
10752 /**
10753  * @class Roo.bootstrap.TableCell
10754  * @extends Roo.bootstrap.Component
10755  * @children Roo.bootstrap.Component
10756  * @parent Roo.bootstrap.TableRow
10757  * Bootstrap TableCell class
10758  * 
10759  * @cfg {String} html cell contain text
10760  * @cfg {String} cls cell class
10761  * @cfg {String} tag cell tag (td|th) default td
10762  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10763  * @cfg {String} align Aligns the content in a cell
10764  * @cfg {String} axis Categorizes cells
10765  * @cfg {String} bgcolor Specifies the background color of a cell
10766  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10767  * @cfg {Number} colspan Specifies the number of columns a cell should span
10768  * @cfg {String} headers Specifies one or more header cells a cell is related to
10769  * @cfg {Number} height Sets the height of a cell
10770  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10771  * @cfg {Number} rowspan Sets the number of rows a cell should span
10772  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10773  * @cfg {String} valign Vertical aligns the content in a cell
10774  * @cfg {Number} width Specifies the width of a cell
10775  * 
10776  * @constructor
10777  * Create a new TableCell
10778  * @param {Object} config The config object
10779  */
10780
10781 Roo.bootstrap.TableCell = function(config){
10782     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10783 };
10784
10785 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10786     
10787     html: false,
10788     cls: false,
10789     tag: false,
10790     abbr: false,
10791     align: false,
10792     axis: false,
10793     bgcolor: false,
10794     charoff: false,
10795     colspan: false,
10796     headers: false,
10797     height: false,
10798     nowrap: false,
10799     rowspan: false,
10800     scope: false,
10801     valign: false,
10802     width: false,
10803     
10804     
10805     getAutoCreate : function(){
10806         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10807         
10808         cfg = {
10809             tag: 'td'
10810         };
10811         
10812         if(this.tag){
10813             cfg.tag = this.tag;
10814         }
10815         
10816         if (this.html) {
10817             cfg.html=this.html
10818         }
10819         if (this.cls) {
10820             cfg.cls=this.cls
10821         }
10822         if (this.abbr) {
10823             cfg.abbr=this.abbr
10824         }
10825         if (this.align) {
10826             cfg.align=this.align
10827         }
10828         if (this.axis) {
10829             cfg.axis=this.axis
10830         }
10831         if (this.bgcolor) {
10832             cfg.bgcolor=this.bgcolor
10833         }
10834         if (this.charoff) {
10835             cfg.charoff=this.charoff
10836         }
10837         if (this.colspan) {
10838             cfg.colspan=this.colspan
10839         }
10840         if (this.headers) {
10841             cfg.headers=this.headers
10842         }
10843         if (this.height) {
10844             cfg.height=this.height
10845         }
10846         if (this.nowrap) {
10847             cfg.nowrap=this.nowrap
10848         }
10849         if (this.rowspan) {
10850             cfg.rowspan=this.rowspan
10851         }
10852         if (this.scope) {
10853             cfg.scope=this.scope
10854         }
10855         if (this.valign) {
10856             cfg.valign=this.valign
10857         }
10858         if (this.width) {
10859             cfg.width=this.width
10860         }
10861         
10862         
10863         return cfg;
10864     }
10865    
10866 });
10867
10868  
10869
10870  /*
10871  * - LGPL
10872  *
10873  * table row
10874  * 
10875  */
10876
10877 /**
10878  * @class Roo.bootstrap.TableRow
10879  * @extends Roo.bootstrap.Component
10880  * @children Roo.bootstrap.TableCell
10881  * @parent Roo.bootstrap.TableBody
10882  * Bootstrap TableRow class
10883  * @cfg {String} cls row class
10884  * @cfg {String} align Aligns the content in a table row
10885  * @cfg {String} bgcolor Specifies a background color for a table row
10886  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10887  * @cfg {String} valign Vertical aligns the content in a table row
10888  * 
10889  * @constructor
10890  * Create a new TableRow
10891  * @param {Object} config The config object
10892  */
10893
10894 Roo.bootstrap.TableRow = function(config){
10895     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10896 };
10897
10898 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10899     
10900     cls: false,
10901     align: false,
10902     bgcolor: false,
10903     charoff: false,
10904     valign: false,
10905     
10906     getAutoCreate : function(){
10907         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10908         
10909         cfg = {
10910             tag: 'tr'
10911         };
10912             
10913         if(this.cls){
10914             cfg.cls = this.cls;
10915         }
10916         if(this.align){
10917             cfg.align = this.align;
10918         }
10919         if(this.bgcolor){
10920             cfg.bgcolor = this.bgcolor;
10921         }
10922         if(this.charoff){
10923             cfg.charoff = this.charoff;
10924         }
10925         if(this.valign){
10926             cfg.valign = this.valign;
10927         }
10928         
10929         return cfg;
10930     }
10931    
10932 });
10933
10934  
10935
10936  /*
10937  * - LGPL
10938  *
10939  * table body
10940  * 
10941  */
10942
10943 /**
10944  * @class Roo.bootstrap.TableBody
10945  * @extends Roo.bootstrap.Component
10946  * @children Roo.bootstrap.TableRow
10947  * @parent Roo.bootstrap.Table
10948  * Bootstrap TableBody class
10949  * @cfg {String} cls element class
10950  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10951  * @cfg {String} align Aligns the content inside the element
10952  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10953  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10954  * 
10955  * @constructor
10956  * Create a new TableBody
10957  * @param {Object} config The config object
10958  */
10959
10960 Roo.bootstrap.TableBody = function(config){
10961     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10962 };
10963
10964 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10965     
10966     cls: false,
10967     tag: false,
10968     align: false,
10969     charoff: false,
10970     valign: false,
10971     
10972     getAutoCreate : function(){
10973         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10974         
10975         cfg = {
10976             tag: 'tbody'
10977         };
10978             
10979         if (this.cls) {
10980             cfg.cls=this.cls
10981         }
10982         if(this.tag){
10983             cfg.tag = this.tag;
10984         }
10985         
10986         if(this.align){
10987             cfg.align = this.align;
10988         }
10989         if(this.charoff){
10990             cfg.charoff = this.charoff;
10991         }
10992         if(this.valign){
10993             cfg.valign = this.valign;
10994         }
10995         
10996         return cfg;
10997     }
10998     
10999     
11000 //    initEvents : function()
11001 //    {
11002 //        
11003 //        if(!this.store){
11004 //            return;
11005 //        }
11006 //        
11007 //        this.store = Roo.factory(this.store, Roo.data);
11008 //        this.store.on('load', this.onLoad, this);
11009 //        
11010 //        this.store.load();
11011 //        
11012 //    },
11013 //    
11014 //    onLoad: function () 
11015 //    {   
11016 //        this.fireEvent('load', this);
11017 //    }
11018 //    
11019 //   
11020 });
11021
11022  
11023
11024  /*
11025  * Based on:
11026  * Ext JS Library 1.1.1
11027  * Copyright(c) 2006-2007, Ext JS, LLC.
11028  *
11029  * Originally Released Under LGPL - original licence link has changed is not relivant.
11030  *
11031  * Fork - LGPL
11032  * <script type="text/javascript">
11033  */
11034
11035 // as we use this in bootstrap.
11036 Roo.namespace('Roo.form');
11037  /**
11038  * @class Roo.form.Action
11039  * Internal Class used to handle form actions
11040  * @constructor
11041  * @param {Roo.form.BasicForm} el The form element or its id
11042  * @param {Object} config Configuration options
11043  */
11044
11045  
11046  
11047 // define the action interface
11048 Roo.form.Action = function(form, options){
11049     this.form = form;
11050     this.options = options || {};
11051 };
11052 /**
11053  * Client Validation Failed
11054  * @const 
11055  */
11056 Roo.form.Action.CLIENT_INVALID = 'client';
11057 /**
11058  * Server Validation Failed
11059  * @const 
11060  */
11061 Roo.form.Action.SERVER_INVALID = 'server';
11062  /**
11063  * Connect to Server Failed
11064  * @const 
11065  */
11066 Roo.form.Action.CONNECT_FAILURE = 'connect';
11067 /**
11068  * Reading Data from Server Failed
11069  * @const 
11070  */
11071 Roo.form.Action.LOAD_FAILURE = 'load';
11072
11073 Roo.form.Action.prototype = {
11074     type : 'default',
11075     failureType : undefined,
11076     response : undefined,
11077     result : undefined,
11078
11079     // interface method
11080     run : function(options){
11081
11082     },
11083
11084     // interface method
11085     success : function(response){
11086
11087     },
11088
11089     // interface method
11090     handleResponse : function(response){
11091
11092     },
11093
11094     // default connection failure
11095     failure : function(response){
11096         
11097         this.response = response;
11098         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11099         this.form.afterAction(this, false);
11100     },
11101
11102     processResponse : function(response){
11103         this.response = response;
11104         if(!response.responseText){
11105             return true;
11106         }
11107         this.result = this.handleResponse(response);
11108         return this.result;
11109     },
11110
11111     // utility functions used internally
11112     getUrl : function(appendParams){
11113         var url = this.options.url || this.form.url || this.form.el.dom.action;
11114         if(appendParams){
11115             var p = this.getParams();
11116             if(p){
11117                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11118             }
11119         }
11120         return url;
11121     },
11122
11123     getMethod : function(){
11124         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11125     },
11126
11127     getParams : function(){
11128         var bp = this.form.baseParams;
11129         var p = this.options.params;
11130         if(p){
11131             if(typeof p == "object"){
11132                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11133             }else if(typeof p == 'string' && bp){
11134                 p += '&' + Roo.urlEncode(bp);
11135             }
11136         }else if(bp){
11137             p = Roo.urlEncode(bp);
11138         }
11139         return p;
11140     },
11141
11142     createCallback : function(){
11143         return {
11144             success: this.success,
11145             failure: this.failure,
11146             scope: this,
11147             timeout: (this.form.timeout*1000),
11148             upload: this.form.fileUpload ? this.success : undefined
11149         };
11150     }
11151 };
11152
11153 Roo.form.Action.Submit = function(form, options){
11154     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11155 };
11156
11157 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11158     type : 'submit',
11159
11160     haveProgress : false,
11161     uploadComplete : false,
11162     
11163     // uploadProgress indicator.
11164     uploadProgress : function()
11165     {
11166         if (!this.form.progressUrl) {
11167             return;
11168         }
11169         
11170         if (!this.haveProgress) {
11171             Roo.MessageBox.progress("Uploading", "Uploading");
11172         }
11173         if (this.uploadComplete) {
11174            Roo.MessageBox.hide();
11175            return;
11176         }
11177         
11178         this.haveProgress = true;
11179    
11180         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11181         
11182         var c = new Roo.data.Connection();
11183         c.request({
11184             url : this.form.progressUrl,
11185             params: {
11186                 id : uid
11187             },
11188             method: 'GET',
11189             success : function(req){
11190                //console.log(data);
11191                 var rdata = false;
11192                 var edata;
11193                 try  {
11194                    rdata = Roo.decode(req.responseText)
11195                 } catch (e) {
11196                     Roo.log("Invalid data from server..");
11197                     Roo.log(edata);
11198                     return;
11199                 }
11200                 if (!rdata || !rdata.success) {
11201                     Roo.log(rdata);
11202                     Roo.MessageBox.alert(Roo.encode(rdata));
11203                     return;
11204                 }
11205                 var data = rdata.data;
11206                 
11207                 if (this.uploadComplete) {
11208                    Roo.MessageBox.hide();
11209                    return;
11210                 }
11211                    
11212                 if (data){
11213                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11214                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11215                     );
11216                 }
11217                 this.uploadProgress.defer(2000,this);
11218             },
11219        
11220             failure: function(data) {
11221                 Roo.log('progress url failed ');
11222                 Roo.log(data);
11223             },
11224             scope : this
11225         });
11226            
11227     },
11228     
11229     
11230     run : function()
11231     {
11232         // run get Values on the form, so it syncs any secondary forms.
11233         this.form.getValues();
11234         
11235         var o = this.options;
11236         var method = this.getMethod();
11237         var isPost = method == 'POST';
11238         if(o.clientValidation === false || this.form.isValid()){
11239             
11240             if (this.form.progressUrl) {
11241                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11242                     (new Date() * 1) + '' + Math.random());
11243                     
11244             } 
11245             
11246             
11247             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11248                 form:this.form.el.dom,
11249                 url:this.getUrl(!isPost),
11250                 method: method,
11251                 params:isPost ? this.getParams() : null,
11252                 isUpload: this.form.fileUpload,
11253                 formData : this.form.formData
11254             }));
11255             
11256             this.uploadProgress();
11257
11258         }else if (o.clientValidation !== false){ // client validation failed
11259             this.failureType = Roo.form.Action.CLIENT_INVALID;
11260             this.form.afterAction(this, false);
11261         }
11262     },
11263
11264     success : function(response)
11265     {
11266         this.uploadComplete= true;
11267         if (this.haveProgress) {
11268             Roo.MessageBox.hide();
11269         }
11270         
11271         
11272         var result = this.processResponse(response);
11273         if(result === true || result.success){
11274             this.form.afterAction(this, true);
11275             return;
11276         }
11277         if(result.errors){
11278             this.form.markInvalid(result.errors);
11279             this.failureType = Roo.form.Action.SERVER_INVALID;
11280         }
11281         this.form.afterAction(this, false);
11282     },
11283     failure : function(response)
11284     {
11285         this.uploadComplete= true;
11286         if (this.haveProgress) {
11287             Roo.MessageBox.hide();
11288         }
11289         
11290         this.response = response;
11291         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11292         this.form.afterAction(this, false);
11293     },
11294     
11295     handleResponse : function(response){
11296         if(this.form.errorReader){
11297             var rs = this.form.errorReader.read(response);
11298             var errors = [];
11299             if(rs.records){
11300                 for(var i = 0, len = rs.records.length; i < len; i++) {
11301                     var r = rs.records[i];
11302                     errors[i] = r.data;
11303                 }
11304             }
11305             if(errors.length < 1){
11306                 errors = null;
11307             }
11308             return {
11309                 success : rs.success,
11310                 errors : errors
11311             };
11312         }
11313         var ret = false;
11314         try {
11315             ret = Roo.decode(response.responseText);
11316         } catch (e) {
11317             ret = {
11318                 success: false,
11319                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11320                 errors : []
11321             };
11322         }
11323         return ret;
11324         
11325     }
11326 });
11327
11328
11329 Roo.form.Action.Load = function(form, options){
11330     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11331     this.reader = this.form.reader;
11332 };
11333
11334 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11335     type : 'load',
11336
11337     run : function(){
11338         
11339         Roo.Ajax.request(Roo.apply(
11340                 this.createCallback(), {
11341                     method:this.getMethod(),
11342                     url:this.getUrl(false),
11343                     params:this.getParams()
11344         }));
11345     },
11346
11347     success : function(response){
11348         
11349         var result = this.processResponse(response);
11350         if(result === true || !result.success || !result.data){
11351             this.failureType = Roo.form.Action.LOAD_FAILURE;
11352             this.form.afterAction(this, false);
11353             return;
11354         }
11355         this.form.clearInvalid();
11356         this.form.setValues(result.data);
11357         this.form.afterAction(this, true);
11358     },
11359
11360     handleResponse : function(response){
11361         if(this.form.reader){
11362             var rs = this.form.reader.read(response);
11363             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11364             return {
11365                 success : rs.success,
11366                 data : data
11367             };
11368         }
11369         return Roo.decode(response.responseText);
11370     }
11371 });
11372
11373 Roo.form.Action.ACTION_TYPES = {
11374     'load' : Roo.form.Action.Load,
11375     'submit' : Roo.form.Action.Submit
11376 };/*
11377  * - LGPL
11378  *
11379  * form
11380  *
11381  */
11382
11383 /**
11384  * @class Roo.bootstrap.form.Form
11385  * @extends Roo.bootstrap.Component
11386  * @children Roo.bootstrap.Component
11387  * Bootstrap Form class
11388  * @cfg {String} method  GET | POST (default POST)
11389  * @cfg {String} labelAlign top | left (default top)
11390  * @cfg {String} align left  | right - for navbars
11391  * @cfg {Boolean} loadMask load mask when submit (default true)
11392
11393  *
11394  * @constructor
11395  * Create a new Form
11396  * @param {Object} config The config object
11397  */
11398
11399
11400 Roo.bootstrap.form.Form = function(config){
11401     
11402     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11403     
11404     Roo.bootstrap.form.Form.popover.apply();
11405     
11406     this.addEvents({
11407         /**
11408          * @event clientvalidation
11409          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11410          * @param {Form} this
11411          * @param {Boolean} valid true if the form has passed client-side validation
11412          */
11413         clientvalidation: true,
11414         /**
11415          * @event beforeaction
11416          * Fires before any action is performed. Return false to cancel the action.
11417          * @param {Form} this
11418          * @param {Action} action The action to be performed
11419          */
11420         beforeaction: true,
11421         /**
11422          * @event actionfailed
11423          * Fires when an action fails.
11424          * @param {Form} this
11425          * @param {Action} action The action that failed
11426          */
11427         actionfailed : true,
11428         /**
11429          * @event actioncomplete
11430          * Fires when an action is completed.
11431          * @param {Form} this
11432          * @param {Action} action The action that completed
11433          */
11434         actioncomplete : true
11435     });
11436 };
11437
11438 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11439
11440      /**
11441      * @cfg {String} method
11442      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11443      */
11444     method : 'POST',
11445     /**
11446      * @cfg {String} url
11447      * The URL to use for form actions if one isn't supplied in the action options.
11448      */
11449     /**
11450      * @cfg {Boolean} fileUpload
11451      * Set to true if this form is a file upload.
11452      */
11453
11454     /**
11455      * @cfg {Object} baseParams
11456      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11457      */
11458
11459     /**
11460      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11461      */
11462     timeout: 30,
11463     /**
11464      * @cfg {Sting} align (left|right) for navbar forms
11465      */
11466     align : 'left',
11467
11468     // private
11469     activeAction : null,
11470
11471     /**
11472      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11473      * element by passing it or its id or mask the form itself by passing in true.
11474      * @type Mixed
11475      */
11476     waitMsgTarget : false,
11477
11478     loadMask : true,
11479     
11480     /**
11481      * @cfg {Boolean} errorMask (true|false) default false
11482      */
11483     errorMask : false,
11484     
11485     /**
11486      * @cfg {Number} maskOffset Default 100
11487      */
11488     maskOffset : 100,
11489     
11490     /**
11491      * @cfg {Boolean} maskBody
11492      */
11493     maskBody : false,
11494
11495     getAutoCreate : function(){
11496
11497         var cfg = {
11498             tag: 'form',
11499             method : this.method || 'POST',
11500             id : this.id || Roo.id(),
11501             cls : ''
11502         };
11503         if (this.parent().xtype.match(/^Nav/)) {
11504             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11505
11506         }
11507
11508         if (this.labelAlign == 'left' ) {
11509             cfg.cls += ' form-horizontal';
11510         }
11511
11512
11513         return cfg;
11514     },
11515     initEvents : function()
11516     {
11517         this.el.on('submit', this.onSubmit, this);
11518         // this was added as random key presses on the form where triggering form submit.
11519         this.el.on('keypress', function(e) {
11520             if (e.getCharCode() != 13) {
11521                 return true;
11522             }
11523             // we might need to allow it for textareas.. and some other items.
11524             // check e.getTarget().
11525
11526             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11527                 return true;
11528             }
11529
11530             Roo.log("keypress blocked");
11531
11532             e.preventDefault();
11533             return false;
11534         });
11535         
11536     },
11537     // private
11538     onSubmit : function(e){
11539         e.stopEvent();
11540     },
11541
11542      /**
11543      * Returns true if client-side validation on the form is successful.
11544      * @return Boolean
11545      */
11546     isValid : function(){
11547         var items = this.getItems();
11548         var valid = true;
11549         var target = false;
11550         
11551         items.each(function(f){
11552             
11553             if(f.validate()){
11554                 return;
11555             }
11556             
11557             Roo.log('invalid field: ' + f.name);
11558             
11559             valid = false;
11560
11561             if(!target && f.el.isVisible(true)){
11562                 target = f;
11563             }
11564            
11565         });
11566         
11567         if(this.errorMask && !valid){
11568             Roo.bootstrap.form.Form.popover.mask(this, target);
11569         }
11570         
11571         return valid;
11572     },
11573     
11574     /**
11575      * Returns true if any fields in this form have changed since their original load.
11576      * @return Boolean
11577      */
11578     isDirty : function(){
11579         var dirty = false;
11580         var items = this.getItems();
11581         items.each(function(f){
11582            if(f.isDirty()){
11583                dirty = true;
11584                return false;
11585            }
11586            return true;
11587         });
11588         return dirty;
11589     },
11590      /**
11591      * Performs a predefined action (submit or load) or custom actions you define on this form.
11592      * @param {String} actionName The name of the action type
11593      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11594      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11595      * accept other config options):
11596      * <pre>
11597 Property          Type             Description
11598 ----------------  ---------------  ----------------------------------------------------------------------------------
11599 url               String           The url for the action (defaults to the form's url)
11600 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11601 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11602 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11603                                    validate the form on the client (defaults to false)
11604      * </pre>
11605      * @return {BasicForm} this
11606      */
11607     doAction : function(action, options){
11608         if(typeof action == 'string'){
11609             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11610         }
11611         if(this.fireEvent('beforeaction', this, action) !== false){
11612             this.beforeAction(action);
11613             action.run.defer(100, action);
11614         }
11615         return this;
11616     },
11617
11618     // private
11619     beforeAction : function(action){
11620         var o = action.options;
11621         
11622         if(this.loadMask){
11623             
11624             if(this.maskBody){
11625                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11626             } else {
11627                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11628             }
11629         }
11630         // not really supported yet.. ??
11631
11632         //if(this.waitMsgTarget === true){
11633         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11634         //}else if(this.waitMsgTarget){
11635         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11636         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637         //}else {
11638         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11639        // }
11640
11641     },
11642
11643     // private
11644     afterAction : function(action, success){
11645         this.activeAction = null;
11646         var o = action.options;
11647
11648         if(this.loadMask){
11649             
11650             if(this.maskBody){
11651                 Roo.get(document.body).unmask();
11652             } else {
11653                 this.el.unmask();
11654             }
11655         }
11656         
11657         //if(this.waitMsgTarget === true){
11658 //            this.el.unmask();
11659         //}else if(this.waitMsgTarget){
11660         //    this.waitMsgTarget.unmask();
11661         //}else{
11662         //    Roo.MessageBox.updateProgress(1);
11663         //    Roo.MessageBox.hide();
11664        // }
11665         //
11666         if(success){
11667             if(o.reset){
11668                 this.reset();
11669             }
11670             Roo.callback(o.success, o.scope, [this, action]);
11671             this.fireEvent('actioncomplete', this, action);
11672
11673         }else{
11674
11675             // failure condition..
11676             // we have a scenario where updates need confirming.
11677             // eg. if a locking scenario exists..
11678             // we look for { errors : { needs_confirm : true }} in the response.
11679             if (
11680                 (typeof(action.result) != 'undefined')  &&
11681                 (typeof(action.result.errors) != 'undefined')  &&
11682                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11683            ){
11684                 var _t = this;
11685                 Roo.log("not supported yet");
11686                  /*
11687
11688                 Roo.MessageBox.confirm(
11689                     "Change requires confirmation",
11690                     action.result.errorMsg,
11691                     function(r) {
11692                         if (r != 'yes') {
11693                             return;
11694                         }
11695                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11696                     }
11697
11698                 );
11699                 */
11700
11701
11702                 return;
11703             }
11704
11705             Roo.callback(o.failure, o.scope, [this, action]);
11706             // show an error message if no failed handler is set..
11707             if (!this.hasListener('actionfailed')) {
11708                 Roo.log("need to add dialog support");
11709                 /*
11710                 Roo.MessageBox.alert("Error",
11711                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11712                         action.result.errorMsg :
11713                         "Saving Failed, please check your entries or try again"
11714                 );
11715                 */
11716             }
11717
11718             this.fireEvent('actionfailed', this, action);
11719         }
11720
11721     },
11722     /**
11723      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11724      * @param {String} id The value to search for
11725      * @return Field
11726      */
11727     findField : function(id){
11728         var items = this.getItems();
11729         var field = items.get(id);
11730         if(!field){
11731              items.each(function(f){
11732                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11733                     field = f;
11734                     return false;
11735                 }
11736                 return true;
11737             });
11738         }
11739         return field || null;
11740     },
11741      /**
11742      * Mark fields in this form invalid in bulk.
11743      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11744      * @return {BasicForm} this
11745      */
11746     markInvalid : function(errors){
11747         if(errors instanceof Array){
11748             for(var i = 0, len = errors.length; i < len; i++){
11749                 var fieldError = errors[i];
11750                 var f = this.findField(fieldError.id);
11751                 if(f){
11752                     f.markInvalid(fieldError.msg);
11753                 }
11754             }
11755         }else{
11756             var field, id;
11757             for(id in errors){
11758                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11759                     field.markInvalid(errors[id]);
11760                 }
11761             }
11762         }
11763         //Roo.each(this.childForms || [], function (f) {
11764         //    f.markInvalid(errors);
11765         //});
11766
11767         return this;
11768     },
11769
11770     /**
11771      * Set values for fields in this form in bulk.
11772      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11773      * @return {BasicForm} this
11774      */
11775     setValues : function(values){
11776         if(values instanceof Array){ // array of objects
11777             for(var i = 0, len = values.length; i < len; i++){
11778                 var v = values[i];
11779                 var f = this.findField(v.id);
11780                 if(f){
11781                     f.setValue(v.value);
11782                     if(this.trackResetOnLoad){
11783                         f.originalValue = f.getValue();
11784                     }
11785                 }
11786             }
11787         }else{ // object hash
11788             var field, id;
11789             for(id in values){
11790                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11791
11792                     if (field.setFromData &&
11793                         field.valueField &&
11794                         field.displayField &&
11795                         // combos' with local stores can
11796                         // be queried via setValue()
11797                         // to set their value..
11798                         (field.store && !field.store.isLocal)
11799                         ) {
11800                         // it's a combo
11801                         var sd = { };
11802                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11803                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11804                         field.setFromData(sd);
11805
11806                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11807                         
11808                         field.setFromData(values);
11809                         
11810                     } else {
11811                         field.setValue(values[id]);
11812                     }
11813
11814
11815                     if(this.trackResetOnLoad){
11816                         field.originalValue = field.getValue();
11817                     }
11818                 }
11819             }
11820         }
11821
11822         //Roo.each(this.childForms || [], function (f) {
11823         //    f.setValues(values);
11824         //});
11825
11826         return this;
11827     },
11828
11829     /**
11830      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11831      * they are returned as an array.
11832      * @param {Boolean} asString
11833      * @return {Object}
11834      */
11835     getValues : function(asString){
11836         //if (this.childForms) {
11837             // copy values from the child forms
11838         //    Roo.each(this.childForms, function (f) {
11839         //        this.setValues(f.getValues());
11840         //    }, this);
11841         //}
11842
11843
11844
11845         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11846         if(asString === true){
11847             return fs;
11848         }
11849         return Roo.urlDecode(fs);
11850     },
11851
11852     /**
11853      * Returns the fields in this form as an object with key/value pairs.
11854      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11855      * @return {Object}
11856      */
11857     getFieldValues : function(with_hidden)
11858     {
11859         var items = this.getItems();
11860         var ret = {};
11861         items.each(function(f){
11862             
11863             if (!f.getName()) {
11864                 return;
11865             }
11866             
11867             var v = f.getValue();
11868             
11869             if (f.inputType =='radio') {
11870                 if (typeof(ret[f.getName()]) == 'undefined') {
11871                     ret[f.getName()] = ''; // empty..
11872                 }
11873
11874                 if (!f.el.dom.checked) {
11875                     return;
11876
11877                 }
11878                 v = f.el.dom.value;
11879
11880             }
11881             
11882             if(f.xtype == 'MoneyField'){
11883                 ret[f.currencyName] = f.getCurrency();
11884             }
11885
11886             // not sure if this supported any more..
11887             if ((typeof(v) == 'object') && f.getRawValue) {
11888                 v = f.getRawValue() ; // dates..
11889             }
11890             // combo boxes where name != hiddenName...
11891             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11892                 ret[f.name] = f.getRawValue();
11893             }
11894             ret[f.getName()] = v;
11895         });
11896
11897         return ret;
11898     },
11899
11900     /**
11901      * Clears all invalid messages in this form.
11902      * @return {BasicForm} this
11903      */
11904     clearInvalid : function(){
11905         var items = this.getItems();
11906
11907         items.each(function(f){
11908            f.clearInvalid();
11909         });
11910
11911         return this;
11912     },
11913
11914     /**
11915      * Resets this form.
11916      * @return {BasicForm} this
11917      */
11918     reset : function(){
11919         var items = this.getItems();
11920         items.each(function(f){
11921             f.reset();
11922         });
11923
11924         Roo.each(this.childForms || [], function (f) {
11925             f.reset();
11926         });
11927
11928
11929         return this;
11930     },
11931     
11932     getItems : function()
11933     {
11934         var r=new Roo.util.MixedCollection(false, function(o){
11935             return o.id || (o.id = Roo.id());
11936         });
11937         var iter = function(el) {
11938             if (el.inputEl) {
11939                 r.add(el);
11940             }
11941             if (!el.items) {
11942                 return;
11943             }
11944             Roo.each(el.items,function(e) {
11945                 iter(e);
11946             });
11947         };
11948
11949         iter(this);
11950         return r;
11951     },
11952     
11953     hideFields : function(items)
11954     {
11955         Roo.each(items, function(i){
11956             
11957             var f = this.findField(i);
11958             
11959             if(!f){
11960                 return;
11961             }
11962             
11963             f.hide();
11964             
11965         }, this);
11966     },
11967     
11968     showFields : function(items)
11969     {
11970         Roo.each(items, function(i){
11971             
11972             var f = this.findField(i);
11973             
11974             if(!f){
11975                 return;
11976             }
11977             
11978             f.show();
11979             
11980         }, this);
11981     }
11982
11983 });
11984
11985 Roo.apply(Roo.bootstrap.form.Form, {
11986     
11987     popover : {
11988         
11989         padding : 5,
11990         
11991         isApplied : false,
11992         
11993         isMasked : false,
11994         
11995         form : false,
11996         
11997         target : false,
11998         
11999         toolTip : false,
12000         
12001         intervalID : false,
12002         
12003         maskEl : false,
12004         
12005         apply : function()
12006         {
12007             if(this.isApplied){
12008                 return;
12009             }
12010             
12011             this.maskEl = {
12012                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12013                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12014                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12015                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12016             };
12017             
12018             this.maskEl.top.enableDisplayMode("block");
12019             this.maskEl.left.enableDisplayMode("block");
12020             this.maskEl.bottom.enableDisplayMode("block");
12021             this.maskEl.right.enableDisplayMode("block");
12022             
12023             this.toolTip = new Roo.bootstrap.Tooltip({
12024                 cls : 'roo-form-error-popover',
12025                 alignment : {
12026                     'left' : ['r-l', [-2,0], 'right'],
12027                     'right' : ['l-r', [2,0], 'left'],
12028                     'bottom' : ['tl-bl', [0,2], 'top'],
12029                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12030                 }
12031             });
12032             
12033             this.toolTip.render(Roo.get(document.body));
12034
12035             this.toolTip.el.enableDisplayMode("block");
12036             
12037             Roo.get(document.body).on('click', function(){
12038                 this.unmask();
12039             }, this);
12040             
12041             Roo.get(document.body).on('touchstart', function(){
12042                 this.unmask();
12043             }, this);
12044             
12045             this.isApplied = true
12046         },
12047         
12048         mask : function(form, target)
12049         {
12050             this.form = form;
12051             
12052             this.target = target;
12053             
12054             if(!this.form.errorMask || !target.el){
12055                 return;
12056             }
12057             
12058             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12059             
12060             Roo.log(scrollable);
12061             
12062             var ot = this.target.el.calcOffsetsTo(scrollable);
12063             
12064             var scrollTo = ot[1] - this.form.maskOffset;
12065             
12066             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12067             
12068             scrollable.scrollTo('top', scrollTo);
12069             
12070             var box = this.target.el.getBox();
12071             Roo.log(box);
12072             var zIndex = Roo.bootstrap.Modal.zIndex++;
12073
12074             
12075             this.maskEl.top.setStyle('position', 'absolute');
12076             this.maskEl.top.setStyle('z-index', zIndex);
12077             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12078             this.maskEl.top.setLeft(0);
12079             this.maskEl.top.setTop(0);
12080             this.maskEl.top.show();
12081             
12082             this.maskEl.left.setStyle('position', 'absolute');
12083             this.maskEl.left.setStyle('z-index', zIndex);
12084             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12085             this.maskEl.left.setLeft(0);
12086             this.maskEl.left.setTop(box.y - this.padding);
12087             this.maskEl.left.show();
12088
12089             this.maskEl.bottom.setStyle('position', 'absolute');
12090             this.maskEl.bottom.setStyle('z-index', zIndex);
12091             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12092             this.maskEl.bottom.setLeft(0);
12093             this.maskEl.bottom.setTop(box.bottom + this.padding);
12094             this.maskEl.bottom.show();
12095
12096             this.maskEl.right.setStyle('position', 'absolute');
12097             this.maskEl.right.setStyle('z-index', zIndex);
12098             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12099             this.maskEl.right.setLeft(box.right + this.padding);
12100             this.maskEl.right.setTop(box.y - this.padding);
12101             this.maskEl.right.show();
12102
12103             this.toolTip.bindEl = this.target.el;
12104
12105             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12106
12107             var tip = this.target.blankText;
12108
12109             if(this.target.getValue() !== '' ) {
12110                 
12111                 if (this.target.invalidText.length) {
12112                     tip = this.target.invalidText;
12113                 } else if (this.target.regexText.length){
12114                     tip = this.target.regexText;
12115                 }
12116             }
12117
12118             this.toolTip.show(tip);
12119
12120             this.intervalID = window.setInterval(function() {
12121                 Roo.bootstrap.form.Form.popover.unmask();
12122             }, 10000);
12123
12124             window.onwheel = function(){ return false;};
12125             
12126             (function(){ this.isMasked = true; }).defer(500, this);
12127             
12128         },
12129         
12130         unmask : function()
12131         {
12132             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12133                 return;
12134             }
12135             
12136             this.maskEl.top.setStyle('position', 'absolute');
12137             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12138             this.maskEl.top.hide();
12139
12140             this.maskEl.left.setStyle('position', 'absolute');
12141             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12142             this.maskEl.left.hide();
12143
12144             this.maskEl.bottom.setStyle('position', 'absolute');
12145             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12146             this.maskEl.bottom.hide();
12147
12148             this.maskEl.right.setStyle('position', 'absolute');
12149             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12150             this.maskEl.right.hide();
12151             
12152             this.toolTip.hide();
12153             
12154             this.toolTip.el.hide();
12155             
12156             window.onwheel = function(){ return true;};
12157             
12158             if(this.intervalID){
12159                 window.clearInterval(this.intervalID);
12160                 this.intervalID = false;
12161             }
12162             
12163             this.isMasked = false;
12164             
12165         }
12166         
12167     }
12168     
12169 });
12170
12171 /*
12172  * Based on:
12173  * Ext JS Library 1.1.1
12174  * Copyright(c) 2006-2007, Ext JS, LLC.
12175  *
12176  * Originally Released Under LGPL - original licence link has changed is not relivant.
12177  *
12178  * Fork - LGPL
12179  * <script type="text/javascript">
12180  */
12181 /**
12182  * @class Roo.form.VTypes
12183  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12184  * @static
12185  */
12186 Roo.form.VTypes = function(){
12187     // closure these in so they are only created once.
12188     var alpha = /^[a-zA-Z_]+$/;
12189     var alphanum = /^[a-zA-Z0-9_]+$/;
12190     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12191     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12192
12193     // All these messages and functions are configurable
12194     return {
12195         /**
12196          * The function used to validate email addresses
12197          * @param {String} value The email address
12198          */
12199         email : function(v){
12200             return email.test(v);
12201         },
12202         /**
12203          * The error text to display when the email validation function returns false
12204          * @type String
12205          */
12206         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12207         /**
12208          * The keystroke filter mask to be applied on email input
12209          * @type RegExp
12210          */
12211         emailMask : /[a-z0-9_\.\-@]/i,
12212
12213         /**
12214          * The function used to validate URLs
12215          * @param {String} value The URL
12216          */
12217         url : function(v){
12218             return url.test(v);
12219         },
12220         /**
12221          * The error text to display when the url validation function returns false
12222          * @type String
12223          */
12224         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12225         
12226         /**
12227          * The function used to validate alpha values
12228          * @param {String} value The value
12229          */
12230         alpha : function(v){
12231             return alpha.test(v);
12232         },
12233         /**
12234          * The error text to display when the alpha validation function returns false
12235          * @type String
12236          */
12237         alphaText : 'This field should only contain letters and _',
12238         /**
12239          * The keystroke filter mask to be applied on alpha input
12240          * @type RegExp
12241          */
12242         alphaMask : /[a-z_]/i,
12243
12244         /**
12245          * The function used to validate alphanumeric values
12246          * @param {String} value The value
12247          */
12248         alphanum : function(v){
12249             return alphanum.test(v);
12250         },
12251         /**
12252          * The error text to display when the alphanumeric validation function returns false
12253          * @type String
12254          */
12255         alphanumText : 'This field should only contain letters, numbers and _',
12256         /**
12257          * The keystroke filter mask to be applied on alphanumeric input
12258          * @type RegExp
12259          */
12260         alphanumMask : /[a-z0-9_]/i
12261     };
12262 }();/*
12263  * - LGPL
12264  *
12265  * Input
12266  * 
12267  */
12268
12269 /**
12270  * @class Roo.bootstrap.form.Input
12271  * @extends Roo.bootstrap.Component
12272  * Bootstrap Input class
12273  * @cfg {Boolean} disabled is it disabled
12274  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12275  * @cfg {String} name name of the input
12276  * @cfg {string} fieldLabel - the label associated
12277  * @cfg {string} placeholder - placeholder to put in text.
12278  * @cfg {string} before - input group add on before
12279  * @cfg {string} after - input group add on after
12280  * @cfg {string} size - (lg|sm) or leave empty..
12281  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12282  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12283  * @cfg {Number} md colspan out of 12 for computer-sized screens
12284  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12285  * @cfg {string} value default value of the input
12286  * @cfg {Number} labelWidth set the width of label 
12287  * @cfg {Number} labellg set the width of label (1-12)
12288  * @cfg {Number} labelmd set the width of label (1-12)
12289  * @cfg {Number} labelsm set the width of label (1-12)
12290  * @cfg {Number} labelxs set the width of label (1-12)
12291  * @cfg {String} labelAlign (top|left)
12292  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12293  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12294  * @cfg {String} indicatorpos (left|right) default left
12295  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12296  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12297  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12298  * @cfg {Roo.bootstrap.Button} before Button to show before
12299  * @cfg {Roo.bootstrap.Button} afterButton to show before
12300  * @cfg {String} align (left|center|right) Default left
12301  * @cfg {Boolean} forceFeedback (true|false) Default false
12302  * 
12303  * @constructor
12304  * Create a new Input
12305  * @param {Object} config The config object
12306  */
12307
12308 Roo.bootstrap.form.Input = function(config){
12309     
12310     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12311     
12312     this.addEvents({
12313         /**
12314          * @event focus
12315          * Fires when this field receives input focus.
12316          * @param {Roo.form.Field} this
12317          */
12318         focus : true,
12319         /**
12320          * @event blur
12321          * Fires when this field loses input focus.
12322          * @param {Roo.form.Field} this
12323          */
12324         blur : true,
12325         /**
12326          * @event specialkey
12327          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12328          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject} e The event object
12331          */
12332         specialkey : true,
12333         /**
12334          * @event change
12335          * Fires just before the field blurs if the field value has changed.
12336          * @param {Roo.form.Field} this
12337          * @param {Mixed} newValue The new value
12338          * @param {Mixed} oldValue The original value
12339          */
12340         change : true,
12341         /**
12342          * @event invalid
12343          * Fires after the field has been marked as invalid.
12344          * @param {Roo.form.Field} this
12345          * @param {String} msg The validation message
12346          */
12347         invalid : true,
12348         /**
12349          * @event valid
12350          * Fires after the field has been validated with no errors.
12351          * @param {Roo.form.Field} this
12352          */
12353         valid : true,
12354          /**
12355          * @event keyup
12356          * Fires after the key up
12357          * @param {Roo.form.Field} this
12358          * @param {Roo.EventObject}  e The event Object
12359          */
12360         keyup : true,
12361         /**
12362          * @event paste
12363          * Fires after the user pastes into input
12364          * @param {Roo.form.Field} this
12365          * @param {Roo.EventObject}  e The event Object
12366          */
12367         paste : true
12368     });
12369 };
12370
12371 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12372      /**
12373      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12374       automatic validation (defaults to "keyup").
12375      */
12376     validationEvent : "keyup",
12377      /**
12378      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12379      */
12380     validateOnBlur : true,
12381     /**
12382      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12383      */
12384     validationDelay : 250,
12385      /**
12386      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12387      */
12388     focusClass : "x-form-focus",  // not needed???
12389     
12390        
12391     /**
12392      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12393      */
12394     invalidClass : "has-warning",
12395     
12396     /**
12397      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12398      */
12399     validClass : "has-success",
12400     
12401     /**
12402      * @cfg {Boolean} hasFeedback (true|false) default true
12403      */
12404     hasFeedback : true,
12405     
12406     /**
12407      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12408      */
12409     invalidFeedbackClass : "glyphicon-warning-sign",
12410     
12411     /**
12412      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12413      */
12414     validFeedbackClass : "glyphicon-ok",
12415     
12416     /**
12417      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12418      */
12419     selectOnFocus : false,
12420     
12421      /**
12422      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12423      */
12424     maskRe : null,
12425        /**
12426      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12427      */
12428     vtype : null,
12429     
12430       /**
12431      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12432      */
12433     disableKeyFilter : false,
12434     
12435        /**
12436      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12437      */
12438     disabled : false,
12439      /**
12440      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12441      */
12442     allowBlank : true,
12443     /**
12444      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12445      */
12446     blankText : "Please complete this mandatory field",
12447     
12448      /**
12449      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12450      */
12451     minLength : 0,
12452     /**
12453      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12454      */
12455     maxLength : Number.MAX_VALUE,
12456     /**
12457      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12458      */
12459     minLengthText : "The minimum length for this field is {0}",
12460     /**
12461      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12462      */
12463     maxLengthText : "The maximum length for this field is {0}",
12464   
12465     
12466     /**
12467      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12468      * If available, this function will be called only after the basic validators all return true, and will be passed the
12469      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12470      */
12471     validator : null,
12472     /**
12473      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12474      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12475      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12476      */
12477     regex : null,
12478     /**
12479      * @cfg {String} regexText -- Depricated - use Invalid Text
12480      */
12481     regexText : "",
12482     
12483     /**
12484      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12485      */
12486     invalidText : "",
12487     
12488     
12489     
12490     autocomplete: false,
12491     
12492     
12493     fieldLabel : '',
12494     inputType : 'text',
12495     
12496     name : false,
12497     placeholder: false,
12498     before : false,
12499     after : false,
12500     size : false,
12501     hasFocus : false,
12502     preventMark: false,
12503     isFormField : true,
12504     value : '',
12505     labelWidth : 2,
12506     labelAlign : false,
12507     readOnly : false,
12508     align : false,
12509     formatedValue : false,
12510     forceFeedback : false,
12511     
12512     indicatorpos : 'left',
12513     
12514     labellg : 0,
12515     labelmd : 0,
12516     labelsm : 0,
12517     labelxs : 0,
12518     
12519     capture : '',
12520     accept : '',
12521     
12522     parentLabelAlign : function()
12523     {
12524         var parent = this;
12525         while (parent.parent()) {
12526             parent = parent.parent();
12527             if (typeof(parent.labelAlign) !='undefined') {
12528                 return parent.labelAlign;
12529             }
12530         }
12531         return 'left';
12532         
12533     },
12534     
12535     getAutoCreate : function()
12536     {
12537         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12538         
12539         var id = Roo.id();
12540         
12541         var cfg = {};
12542         
12543         if(this.inputType != 'hidden'){
12544             cfg.cls = 'form-group' //input-group
12545         }
12546         
12547         var input =  {
12548             tag: 'input',
12549             id : id,
12550             type : this.inputType,
12551             value : this.value,
12552             cls : 'form-control',
12553             placeholder : this.placeholder || '',
12554             autocomplete : this.autocomplete || 'new-password'
12555         };
12556         if (this.inputType == 'file') {
12557             input.style = 'overflow:hidden'; // why not in CSS?
12558         }
12559         
12560         if(this.capture.length){
12561             input.capture = this.capture;
12562         }
12563         
12564         if(this.accept.length){
12565             input.accept = this.accept + "/*";
12566         }
12567         
12568         if(this.align){
12569             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12570         }
12571         
12572         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12573             input.maxLength = this.maxLength;
12574         }
12575         
12576         if (this.disabled) {
12577             input.disabled=true;
12578         }
12579         
12580         if (this.readOnly) {
12581             input.readonly=true;
12582         }
12583         
12584         if (this.name) {
12585             input.name = this.name;
12586         }
12587         
12588         if (this.size) {
12589             input.cls += ' input-' + this.size;
12590         }
12591         
12592         var settings=this;
12593         ['xs','sm','md','lg'].map(function(size){
12594             if (settings[size]) {
12595                 cfg.cls += ' col-' + size + '-' + settings[size];
12596             }
12597         });
12598         
12599         var inputblock = input;
12600         
12601         var feedback = {
12602             tag: 'span',
12603             cls: 'glyphicon form-control-feedback'
12604         };
12605             
12606         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12607             
12608             inputblock = {
12609                 cls : 'has-feedback',
12610                 cn :  [
12611                     input,
12612                     feedback
12613                 ] 
12614             };  
12615         }
12616         
12617         if (this.before || this.after) {
12618             
12619             inputblock = {
12620                 cls : 'input-group',
12621                 cn :  [] 
12622             };
12623             
12624             if (this.before && typeof(this.before) == 'string') {
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12629                     html : this.before
12630                 });
12631             }
12632             if (this.before && typeof(this.before) == 'object') {
12633                 this.before = Roo.factory(this.before);
12634                 
12635                 inputblock.cn.push({
12636                     tag :'span',
12637                     cls : 'roo-input-before input-group-prepend   input-group-' +
12638                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12639                 });
12640             }
12641             
12642             inputblock.cn.push(input);
12643             
12644             if (this.after && typeof(this.after) == 'string') {
12645                 inputblock.cn.push({
12646                     tag :'span',
12647                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12648                     html : this.after
12649                 });
12650             }
12651             if (this.after && typeof(this.after) == 'object') {
12652                 this.after = Roo.factory(this.after);
12653                 
12654                 inputblock.cn.push({
12655                     tag :'span',
12656                     cls : 'roo-input-after input-group-append  input-group-' +
12657                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12658                 });
12659             }
12660             
12661             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12662                 inputblock.cls += ' has-feedback';
12663                 inputblock.cn.push(feedback);
12664             }
12665         };
12666         var indicator = {
12667             tag : 'i',
12668             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12669             tooltip : 'This field is required'
12670         };
12671         if (this.allowBlank ) {
12672             indicator.style = this.allowBlank ? ' display:none' : '';
12673         }
12674         if (align ==='left' && this.fieldLabel.length) {
12675             
12676             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12677             
12678             cfg.cn = [
12679                 indicator,
12680                 {
12681                     tag: 'label',
12682                     'for' :  id,
12683                     cls : 'control-label col-form-label',
12684                     html : this.fieldLabel
12685
12686                 },
12687                 {
12688                     cls : "", 
12689                     cn: [
12690                         inputblock
12691                     ]
12692                 }
12693             ];
12694             
12695             var labelCfg = cfg.cn[1];
12696             var contentCfg = cfg.cn[2];
12697             
12698             if(this.indicatorpos == 'right'){
12699                 cfg.cn = [
12700                     {
12701                         tag: 'label',
12702                         'for' :  id,
12703                         cls : 'control-label col-form-label',
12704                         cn : [
12705                             {
12706                                 tag : 'span',
12707                                 html : this.fieldLabel
12708                             },
12709                             indicator
12710                         ]
12711                     },
12712                     {
12713                         cls : "",
12714                         cn: [
12715                             inputblock
12716                         ]
12717                     }
12718
12719                 ];
12720                 
12721                 labelCfg = cfg.cn[0];
12722                 contentCfg = cfg.cn[1];
12723             
12724             }
12725             
12726             if(this.labelWidth > 12){
12727                 labelCfg.style = "width: " + this.labelWidth + 'px';
12728             }
12729             
12730             if(this.labelWidth < 13 && this.labelmd == 0){
12731                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12732             }
12733             
12734             if(this.labellg > 0){
12735                 labelCfg.cls += ' col-lg-' + this.labellg;
12736                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12737             }
12738             
12739             if(this.labelmd > 0){
12740                 labelCfg.cls += ' col-md-' + this.labelmd;
12741                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12742             }
12743             
12744             if(this.labelsm > 0){
12745                 labelCfg.cls += ' col-sm-' + this.labelsm;
12746                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12747             }
12748             
12749             if(this.labelxs > 0){
12750                 labelCfg.cls += ' col-xs-' + this.labelxs;
12751                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12752             }
12753             
12754             
12755         } else if ( this.fieldLabel.length) {
12756                 
12757             
12758             
12759             cfg.cn = [
12760                 {
12761                     tag : 'i',
12762                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12763                     tooltip : 'This field is required',
12764                     style : this.allowBlank ? ' display:none' : '' 
12765                 },
12766                 {
12767                     tag: 'label',
12768                    //cls : 'input-group-addon',
12769                     html : this.fieldLabel
12770
12771                 },
12772
12773                inputblock
12774
12775            ];
12776            
12777            if(this.indicatorpos == 'right'){
12778        
12779                 cfg.cn = [
12780                     {
12781                         tag: 'label',
12782                        //cls : 'input-group-addon',
12783                         html : this.fieldLabel
12784
12785                     },
12786                     {
12787                         tag : 'i',
12788                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12789                         tooltip : 'This field is required',
12790                         style : this.allowBlank ? ' display:none' : '' 
12791                     },
12792
12793                    inputblock
12794
12795                ];
12796
12797             }
12798
12799         } else {
12800             
12801             cfg.cn = [
12802
12803                     inputblock
12804
12805             ];
12806                 
12807                 
12808         };
12809         
12810         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12811            cfg.cls += ' navbar-form';
12812         }
12813         
12814         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12815             // on BS4 we do this only if not form 
12816             cfg.cls += ' navbar-form';
12817             cfg.tag = 'li';
12818         }
12819         
12820         return cfg;
12821         
12822     },
12823     /**
12824      * return the real input element.
12825      */
12826     inputEl: function ()
12827     {
12828         return this.el.select('input.form-control',true).first();
12829     },
12830     
12831     tooltipEl : function()
12832     {
12833         return this.inputEl();
12834     },
12835     
12836     indicatorEl : function()
12837     {
12838         if (Roo.bootstrap.version == 4) {
12839             return false; // not enabled in v4 yet.
12840         }
12841         
12842         var indicator = this.el.select('i.roo-required-indicator',true).first();
12843         
12844         if(!indicator){
12845             return false;
12846         }
12847         
12848         return indicator;
12849         
12850     },
12851     
12852     setDisabled : function(v)
12853     {
12854         var i  = this.inputEl().dom;
12855         if (!v) {
12856             i.removeAttribute('disabled');
12857             return;
12858             
12859         }
12860         i.setAttribute('disabled','true');
12861     },
12862     initEvents : function()
12863     {
12864           
12865         this.inputEl().on("keydown" , this.fireKey,  this);
12866         this.inputEl().on("focus", this.onFocus,  this);
12867         this.inputEl().on("blur", this.onBlur,  this);
12868         
12869         this.inputEl().relayEvent('keyup', this);
12870         this.inputEl().relayEvent('paste', this);
12871         
12872         this.indicator = this.indicatorEl();
12873         
12874         if(this.indicator){
12875             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12876         }
12877  
12878         // reference to original value for reset
12879         this.originalValue = this.getValue();
12880         //Roo.form.TextField.superclass.initEvents.call(this);
12881         if(this.validationEvent == 'keyup'){
12882             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12883             this.inputEl().on('keyup', this.filterValidation, this);
12884         }
12885         else if(this.validationEvent !== false){
12886             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12887         }
12888         
12889         if(this.selectOnFocus){
12890             this.on("focus", this.preFocus, this);
12891             
12892         }
12893         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12894             this.inputEl().on("keypress", this.filterKeys, this);
12895         } else {
12896             this.inputEl().relayEvent('keypress', this);
12897         }
12898        /* if(this.grow){
12899             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12900             this.el.on("click", this.autoSize,  this);
12901         }
12902         */
12903         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12904             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12905         }
12906         
12907         if (typeof(this.before) == 'object') {
12908             this.before.render(this.el.select('.roo-input-before',true).first());
12909         }
12910         if (typeof(this.after) == 'object') {
12911             this.after.render(this.el.select('.roo-input-after',true).first());
12912         }
12913         
12914         this.inputEl().on('change', this.onChange, this);
12915         
12916     },
12917     filterValidation : function(e){
12918         if(!e.isNavKeyPress()){
12919             this.validationTask.delay(this.validationDelay);
12920         }
12921     },
12922      /**
12923      * Validates the field value
12924      * @return {Boolean} True if the value is valid, else false
12925      */
12926     validate : function(){
12927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12928         if(this.disabled || this.validateValue(this.getRawValue())){
12929             this.markValid();
12930             return true;
12931         }
12932         
12933         this.markInvalid();
12934         return false;
12935     },
12936     
12937     
12938     /**
12939      * Validates a value according to the field's validation rules and marks the field as invalid
12940      * if the validation fails
12941      * @param {Mixed} value The value to validate
12942      * @return {Boolean} True if the value is valid, else false
12943      */
12944     validateValue : function(value)
12945     {
12946         if(this.getVisibilityEl().hasClass('hidden')){
12947             return true;
12948         }
12949         
12950         if(value.length < 1)  { // if it's blank
12951             if(this.allowBlank){
12952                 return true;
12953             }
12954             return false;
12955         }
12956         
12957         if(value.length < this.minLength){
12958             return false;
12959         }
12960         if(value.length > this.maxLength){
12961             return false;
12962         }
12963         if(this.vtype){
12964             var vt = Roo.form.VTypes;
12965             if(!vt[this.vtype](value, this)){
12966                 return false;
12967             }
12968         }
12969         if(typeof this.validator == "function"){
12970             var msg = this.validator(value);
12971             if (typeof(msg) == 'string') {
12972                 this.invalidText = msg;
12973             }
12974             if(msg !== true){
12975                 return false;
12976             }
12977         }
12978         
12979         if(this.regex && !this.regex.test(value)){
12980             return false;
12981         }
12982         
12983         return true;
12984     },
12985     
12986      // private
12987     fireKey : function(e){
12988         //Roo.log('field ' + e.getKey());
12989         if(e.isNavKeyPress()){
12990             this.fireEvent("specialkey", this, e);
12991         }
12992     },
12993     focus : function (selectText){
12994         if(this.rendered){
12995             this.inputEl().focus();
12996             if(selectText === true){
12997                 this.inputEl().dom.select();
12998             }
12999         }
13000         return this;
13001     } ,
13002     
13003     onFocus : function(){
13004         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13005            // this.el.addClass(this.focusClass);
13006         }
13007         if(!this.hasFocus){
13008             this.hasFocus = true;
13009             this.startValue = this.getValue();
13010             this.fireEvent("focus", this);
13011         }
13012     },
13013     
13014     beforeBlur : Roo.emptyFn,
13015
13016     
13017     // private
13018     onBlur : function(){
13019         this.beforeBlur();
13020         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13021             //this.el.removeClass(this.focusClass);
13022         }
13023         this.hasFocus = false;
13024         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13025             this.validate();
13026         }
13027         var v = this.getValue();
13028         if(String(v) !== String(this.startValue)){
13029             this.fireEvent('change', this, v, this.startValue);
13030         }
13031         this.fireEvent("blur", this);
13032     },
13033     
13034     onChange : function(e)
13035     {
13036         var v = this.getValue();
13037         if(String(v) !== String(this.startValue)){
13038             this.fireEvent('change', this, v, this.startValue);
13039         }
13040         
13041     },
13042     
13043     /**
13044      * Resets the current field value to the originally loaded value and clears any validation messages
13045      */
13046     reset : function(){
13047         this.setValue(this.originalValue);
13048         this.validate();
13049     },
13050      /**
13051      * Returns the name of the field
13052      * @return {Mixed} name The name field
13053      */
13054     getName: function(){
13055         return this.name;
13056     },
13057      /**
13058      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13059      * @return {Mixed} value The field value
13060      */
13061     getValue : function(){
13062         
13063         var v = this.inputEl().getValue();
13064         
13065         return v;
13066     },
13067     /**
13068      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13069      * @return {Mixed} value The field value
13070      */
13071     getRawValue : function(){
13072         var v = this.inputEl().getValue();
13073         
13074         return v;
13075     },
13076     
13077     /**
13078      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13079      * @param {Mixed} value The value to set
13080      */
13081     setRawValue : function(v){
13082         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13083     },
13084     
13085     selectText : function(start, end){
13086         var v = this.getRawValue();
13087         if(v.length > 0){
13088             start = start === undefined ? 0 : start;
13089             end = end === undefined ? v.length : end;
13090             var d = this.inputEl().dom;
13091             if(d.setSelectionRange){
13092                 d.setSelectionRange(start, end);
13093             }else if(d.createTextRange){
13094                 var range = d.createTextRange();
13095                 range.moveStart("character", start);
13096                 range.moveEnd("character", v.length-end);
13097                 range.select();
13098             }
13099         }
13100     },
13101     
13102     /**
13103      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13104      * @param {Mixed} value The value to set
13105      */
13106     setValue : function(v){
13107         this.value = v;
13108         if(this.rendered){
13109             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13110             this.validate();
13111         }
13112     },
13113     
13114     /*
13115     processValue : function(value){
13116         if(this.stripCharsRe){
13117             var newValue = value.replace(this.stripCharsRe, '');
13118             if(newValue !== value){
13119                 this.setRawValue(newValue);
13120                 return newValue;
13121             }
13122         }
13123         return value;
13124     },
13125   */
13126     preFocus : function(){
13127         
13128         if(this.selectOnFocus){
13129             this.inputEl().dom.select();
13130         }
13131     },
13132     filterKeys : function(e){
13133         var k = e.getKey();
13134         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13135             return;
13136         }
13137         var c = e.getCharCode(), cc = String.fromCharCode(c);
13138         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13139             return;
13140         }
13141         if(!this.maskRe.test(cc)){
13142             e.stopEvent();
13143         }
13144     },
13145      /**
13146      * Clear any invalid styles/messages for this field
13147      */
13148     clearInvalid : function(){
13149         
13150         if(!this.el || this.preventMark){ // not rendered
13151             return;
13152         }
13153         
13154         
13155         this.el.removeClass([this.invalidClass, 'is-invalid']);
13156         
13157         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13158             
13159             var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161             if(feedback){
13162                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13163             }
13164             
13165         }
13166         
13167         if(this.indicator){
13168             this.indicator.removeClass('visible');
13169             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13170         }
13171         
13172         this.fireEvent('valid', this);
13173     },
13174     
13175      /**
13176      * Mark this field as valid
13177      */
13178     markValid : function()
13179     {
13180         if(!this.el  || this.preventMark){ // not rendered...
13181             return;
13182         }
13183         
13184         this.el.removeClass([this.invalidClass, this.validClass]);
13185         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13186
13187         var feedback = this.el.select('.form-control-feedback', true).first();
13188             
13189         if(feedback){
13190             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13191         }
13192         
13193         if(this.indicator){
13194             this.indicator.removeClass('visible');
13195             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13196         }
13197         
13198         if(this.disabled){
13199             return;
13200         }
13201         
13202            
13203         if(this.allowBlank && !this.getRawValue().length){
13204             return;
13205         }
13206         if (Roo.bootstrap.version == 3) {
13207             this.el.addClass(this.validClass);
13208         } else {
13209             this.inputEl().addClass('is-valid');
13210         }
13211
13212         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13213             
13214             var feedback = this.el.select('.form-control-feedback', true).first();
13215             
13216             if(feedback){
13217                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13218                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13219             }
13220             
13221         }
13222         
13223         this.fireEvent('valid', this);
13224     },
13225     
13226      /**
13227      * Mark this field as invalid
13228      * @param {String} msg The validation message
13229      */
13230     markInvalid : function(msg)
13231     {
13232         if(!this.el  || this.preventMark){ // not rendered
13233             return;
13234         }
13235         
13236         this.el.removeClass([this.invalidClass, this.validClass]);
13237         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13238         
13239         var feedback = this.el.select('.form-control-feedback', true).first();
13240             
13241         if(feedback){
13242             this.el.select('.form-control-feedback', true).first().removeClass(
13243                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13244         }
13245
13246         if(this.disabled){
13247             return;
13248         }
13249         
13250         if(this.allowBlank && !this.getRawValue().length){
13251             return;
13252         }
13253         
13254         if(this.indicator){
13255             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13256             this.indicator.addClass('visible');
13257         }
13258         if (Roo.bootstrap.version == 3) {
13259             this.el.addClass(this.invalidClass);
13260         } else {
13261             this.inputEl().addClass('is-invalid');
13262         }
13263         
13264         
13265         
13266         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13267             
13268             var feedback = this.el.select('.form-control-feedback', true).first();
13269             
13270             if(feedback){
13271                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13272                 
13273                 if(this.getValue().length || this.forceFeedback){
13274                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13275                 }
13276                 
13277             }
13278             
13279         }
13280         
13281         this.fireEvent('invalid', this, msg);
13282     },
13283     // private
13284     SafariOnKeyDown : function(event)
13285     {
13286         // this is a workaround for a password hang bug on chrome/ webkit.
13287         if (this.inputEl().dom.type != 'password') {
13288             return;
13289         }
13290         
13291         var isSelectAll = false;
13292         
13293         if(this.inputEl().dom.selectionEnd > 0){
13294             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13295         }
13296         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13297             event.preventDefault();
13298             this.setValue('');
13299             return;
13300         }
13301         
13302         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13303             
13304             event.preventDefault();
13305             // this is very hacky as keydown always get's upper case.
13306             //
13307             var cc = String.fromCharCode(event.getCharCode());
13308             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13309             
13310         }
13311     },
13312     adjustWidth : function(tag, w){
13313         tag = tag.toLowerCase();
13314         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13315             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13316                 if(tag == 'input'){
13317                     return w + 2;
13318                 }
13319                 if(tag == 'textarea'){
13320                     return w-2;
13321                 }
13322             }else if(Roo.isOpera){
13323                 if(tag == 'input'){
13324                     return w + 2;
13325                 }
13326                 if(tag == 'textarea'){
13327                     return w-2;
13328                 }
13329             }
13330         }
13331         return w;
13332     },
13333     
13334     setFieldLabel : function(v)
13335     {
13336         if(!this.rendered){
13337             return;
13338         }
13339         
13340         if(this.indicatorEl()){
13341             var ar = this.el.select('label > span',true);
13342             
13343             if (ar.elements.length) {
13344                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13345                 this.fieldLabel = v;
13346                 return;
13347             }
13348             
13349             var br = this.el.select('label',true);
13350             
13351             if(br.elements.length) {
13352                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13353                 this.fieldLabel = v;
13354                 return;
13355             }
13356             
13357             Roo.log('Cannot Found any of label > span || label in input');
13358             return;
13359         }
13360         
13361         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13362         this.fieldLabel = v;
13363         
13364         
13365     }
13366 });
13367
13368  
13369 /*
13370  * - LGPL
13371  *
13372  * Input
13373  * 
13374  */
13375
13376 /**
13377  * @class Roo.bootstrap.form.TextArea
13378  * @extends Roo.bootstrap.form.Input
13379  * Bootstrap TextArea class
13380  * @cfg {Number} cols Specifies the visible width of a text area
13381  * @cfg {Number} rows Specifies the visible number of lines in a text area
13382  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13383  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13384  * @cfg {string} html text
13385  * 
13386  * @constructor
13387  * Create a new TextArea
13388  * @param {Object} config The config object
13389  */
13390
13391 Roo.bootstrap.form.TextArea = function(config){
13392     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13393    
13394 };
13395
13396 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13397      
13398     cols : false,
13399     rows : 5,
13400     readOnly : false,
13401     warp : 'soft',
13402     resize : false,
13403     value: false,
13404     html: false,
13405     
13406     getAutoCreate : function(){
13407         
13408         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13409         
13410         var id = Roo.id();
13411         
13412         var cfg = {};
13413         
13414         if(this.inputType != 'hidden'){
13415             cfg.cls = 'form-group' //input-group
13416         }
13417         
13418         var input =  {
13419             tag: 'textarea',
13420             id : id,
13421             warp : this.warp,
13422             rows : this.rows,
13423             value : this.value || '',
13424             html: this.html || '',
13425             cls : 'form-control',
13426             placeholder : this.placeholder || '' 
13427             
13428         };
13429         
13430         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13431             input.maxLength = this.maxLength;
13432         }
13433         
13434         if(this.resize){
13435             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13436         }
13437         
13438         if(this.cols){
13439             input.cols = this.cols;
13440         }
13441         
13442         if (this.readOnly) {
13443             input.readonly = true;
13444         }
13445         
13446         if (this.name) {
13447             input.name = this.name;
13448         }
13449         
13450         if (this.size) {
13451             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13452         }
13453         
13454         var settings=this;
13455         ['xs','sm','md','lg'].map(function(size){
13456             if (settings[size]) {
13457                 cfg.cls += ' col-' + size + '-' + settings[size];
13458             }
13459         });
13460         
13461         var inputblock = input;
13462         
13463         if(this.hasFeedback && !this.allowBlank){
13464             
13465             var feedback = {
13466                 tag: 'span',
13467                 cls: 'glyphicon form-control-feedback'
13468             };
13469
13470             inputblock = {
13471                 cls : 'has-feedback',
13472                 cn :  [
13473                     input,
13474                     feedback
13475                 ] 
13476             };  
13477         }
13478         
13479         
13480         if (this.before || this.after) {
13481             
13482             inputblock = {
13483                 cls : 'input-group',
13484                 cn :  [] 
13485             };
13486             if (this.before) {
13487                 inputblock.cn.push({
13488                     tag :'span',
13489                     cls : 'input-group-addon',
13490                     html : this.before
13491                 });
13492             }
13493             
13494             inputblock.cn.push(input);
13495             
13496             if(this.hasFeedback && !this.allowBlank){
13497                 inputblock.cls += ' has-feedback';
13498                 inputblock.cn.push(feedback);
13499             }
13500             
13501             if (this.after) {
13502                 inputblock.cn.push({
13503                     tag :'span',
13504                     cls : 'input-group-addon',
13505                     html : this.after
13506                 });
13507             }
13508             
13509         }
13510         
13511         if (align ==='left' && this.fieldLabel.length) {
13512             cfg.cn = [
13513                 {
13514                     tag: 'label',
13515                     'for' :  id,
13516                     cls : 'control-label',
13517                     html : this.fieldLabel
13518                 },
13519                 {
13520                     cls : "",
13521                     cn: [
13522                         inputblock
13523                     ]
13524                 }
13525
13526             ];
13527             
13528             if(this.labelWidth > 12){
13529                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13530             }
13531
13532             if(this.labelWidth < 13 && this.labelmd == 0){
13533                 this.labelmd = this.labelWidth;
13534             }
13535
13536             if(this.labellg > 0){
13537                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13538                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13539             }
13540
13541             if(this.labelmd > 0){
13542                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13543                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13544             }
13545
13546             if(this.labelsm > 0){
13547                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13548                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13549             }
13550
13551             if(this.labelxs > 0){
13552                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13553                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13554             }
13555             
13556         } else if ( this.fieldLabel.length) {
13557             cfg.cn = [
13558
13559                {
13560                    tag: 'label',
13561                    //cls : 'input-group-addon',
13562                    html : this.fieldLabel
13563
13564                },
13565
13566                inputblock
13567
13568            ];
13569
13570         } else {
13571
13572             cfg.cn = [
13573
13574                 inputblock
13575
13576             ];
13577                 
13578         }
13579         
13580         if (this.disabled) {
13581             input.disabled=true;
13582         }
13583         
13584         return cfg;
13585         
13586     },
13587     /**
13588      * return the real textarea element.
13589      */
13590     inputEl: function ()
13591     {
13592         return this.el.select('textarea.form-control',true).first();
13593     },
13594     
13595     /**
13596      * Clear any invalid styles/messages for this field
13597      */
13598     clearInvalid : function()
13599     {
13600         
13601         if(!this.el || this.preventMark){ // not rendered
13602             return;
13603         }
13604         
13605         var label = this.el.select('label', true).first();
13606         var icon = this.el.select('i.fa-star', true).first();
13607         
13608         if(label && icon){
13609             icon.remove();
13610         }
13611         this.el.removeClass( this.validClass);
13612         this.inputEl().removeClass('is-invalid');
13613          
13614         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13615             
13616             var feedback = this.el.select('.form-control-feedback', true).first();
13617             
13618             if(feedback){
13619                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13620             }
13621             
13622         }
13623         
13624         this.fireEvent('valid', this);
13625     },
13626     
13627      /**
13628      * Mark this field as valid
13629      */
13630     markValid : function()
13631     {
13632         if(!this.el  || this.preventMark){ // not rendered
13633             return;
13634         }
13635         
13636         this.el.removeClass([this.invalidClass, this.validClass]);
13637         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13638         
13639         var feedback = this.el.select('.form-control-feedback', true).first();
13640             
13641         if(feedback){
13642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13643         }
13644
13645         if(this.disabled || this.allowBlank){
13646             return;
13647         }
13648         
13649         var label = this.el.select('label', true).first();
13650         var icon = this.el.select('i.fa-star', true).first();
13651         
13652         if(label && icon){
13653             icon.remove();
13654         }
13655         if (Roo.bootstrap.version == 3) {
13656             this.el.addClass(this.validClass);
13657         } else {
13658             this.inputEl().addClass('is-valid');
13659         }
13660         
13661         
13662         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13663             
13664             var feedback = this.el.select('.form-control-feedback', true).first();
13665             
13666             if(feedback){
13667                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13668                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13669             }
13670             
13671         }
13672         
13673         this.fireEvent('valid', this);
13674     },
13675     
13676      /**
13677      * Mark this field as invalid
13678      * @param {String} msg The validation message
13679      */
13680     markInvalid : function(msg)
13681     {
13682         if(!this.el  || this.preventMark){ // not rendered
13683             return;
13684         }
13685         
13686         this.el.removeClass([this.invalidClass, this.validClass]);
13687         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13688         
13689         var feedback = this.el.select('.form-control-feedback', true).first();
13690             
13691         if(feedback){
13692             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13693         }
13694
13695         if(this.disabled || this.allowBlank){
13696             return;
13697         }
13698         
13699         var label = this.el.select('label', true).first();
13700         var icon = this.el.select('i.fa-star', true).first();
13701         
13702         if(!this.getValue().length && label && !icon){
13703             this.el.createChild({
13704                 tag : 'i',
13705                 cls : 'text-danger fa fa-lg fa-star',
13706                 tooltip : 'This field is required',
13707                 style : 'margin-right:5px;'
13708             }, label, true);
13709         }
13710         
13711         if (Roo.bootstrap.version == 3) {
13712             this.el.addClass(this.invalidClass);
13713         } else {
13714             this.inputEl().addClass('is-invalid');
13715         }
13716         
13717         // fixme ... this may be depricated need to test..
13718         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13719             
13720             var feedback = this.el.select('.form-control-feedback', true).first();
13721             
13722             if(feedback){
13723                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13724                 
13725                 if(this.getValue().length || this.forceFeedback){
13726                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13727                 }
13728                 
13729             }
13730             
13731         }
13732         
13733         this.fireEvent('invalid', this, msg);
13734     }
13735 });
13736
13737  
13738 /*
13739  * - LGPL
13740  *
13741  * trigger field - base class for combo..
13742  * 
13743  */
13744  
13745 /**
13746  * @class Roo.bootstrap.form.TriggerField
13747  * @extends Roo.bootstrap.form.Input
13748  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13749  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13750  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13751  * for which you can provide a custom implementation.  For example:
13752  * <pre><code>
13753 var trigger = new Roo.bootstrap.form.TriggerField();
13754 trigger.onTriggerClick = myTriggerFn;
13755 trigger.applyTo('my-field');
13756 </code></pre>
13757  *
13758  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13759  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13760  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13761  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13762  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13763
13764  * @constructor
13765  * Create a new TriggerField.
13766  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13767  * to the base TextField)
13768  */
13769 Roo.bootstrap.form.TriggerField = function(config){
13770     this.mimicing = false;
13771     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13772 };
13773
13774 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13775     /**
13776      * @cfg {String} triggerClass A CSS class to apply to the trigger
13777      */
13778      /**
13779      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13780      */
13781     hideTrigger:false,
13782
13783     /**
13784      * @cfg {Boolean} removable (true|false) special filter default false
13785      */
13786     removable : false,
13787     
13788     /** @cfg {Boolean} grow @hide */
13789     /** @cfg {Number} growMin @hide */
13790     /** @cfg {Number} growMax @hide */
13791
13792     /**
13793      * @hide 
13794      * @method
13795      */
13796     autoSize: Roo.emptyFn,
13797     // private
13798     monitorTab : true,
13799     // private
13800     deferHeight : true,
13801
13802     
13803     actionMode : 'wrap',
13804     
13805     caret : false,
13806     
13807     
13808     getAutoCreate : function(){
13809        
13810         var align = this.labelAlign || this.parentLabelAlign();
13811         
13812         var id = Roo.id();
13813         
13814         var cfg = {
13815             cls: 'form-group' //input-group
13816         };
13817         
13818         
13819         var input =  {
13820             tag: 'input',
13821             id : id,
13822             type : this.inputType,
13823             cls : 'form-control',
13824             autocomplete: 'new-password',
13825             placeholder : this.placeholder || '' 
13826             
13827         };
13828         if (this.name) {
13829             input.name = this.name;
13830         }
13831         if (this.size) {
13832             input.cls += ' input-' + this.size;
13833         }
13834         
13835         if (this.disabled) {
13836             input.disabled=true;
13837         }
13838         
13839         var inputblock = input;
13840         
13841         if(this.hasFeedback && !this.allowBlank){
13842             
13843             var feedback = {
13844                 tag: 'span',
13845                 cls: 'glyphicon form-control-feedback'
13846             };
13847             
13848             if(this.removable && !this.editable  ){
13849                 inputblock = {
13850                     cls : 'has-feedback',
13851                     cn :  [
13852                         inputblock,
13853                         {
13854                             tag: 'button',
13855                             html : 'x',
13856                             cls : 'roo-combo-removable-btn close'
13857                         },
13858                         feedback
13859                     ] 
13860                 };
13861             } else {
13862                 inputblock = {
13863                     cls : 'has-feedback',
13864                     cn :  [
13865                         inputblock,
13866                         feedback
13867                     ] 
13868                 };
13869             }
13870
13871         } else {
13872             if(this.removable && !this.editable ){
13873                 inputblock = {
13874                     cls : 'roo-removable',
13875                     cn :  [
13876                         inputblock,
13877                         {
13878                             tag: 'button',
13879                             html : 'x',
13880                             cls : 'roo-combo-removable-btn close'
13881                         }
13882                     ] 
13883                 };
13884             }
13885         }
13886         
13887         if (this.before || this.after) {
13888             
13889             inputblock = {
13890                 cls : 'input-group',
13891                 cn :  [] 
13892             };
13893             if (this.before) {
13894                 inputblock.cn.push({
13895                     tag :'span',
13896                     cls : 'input-group-addon input-group-prepend input-group-text',
13897                     html : this.before
13898                 });
13899             }
13900             
13901             inputblock.cn.push(input);
13902             
13903             if(this.hasFeedback && !this.allowBlank){
13904                 inputblock.cls += ' has-feedback';
13905                 inputblock.cn.push(feedback);
13906             }
13907             
13908             if (this.after) {
13909                 inputblock.cn.push({
13910                     tag :'span',
13911                     cls : 'input-group-addon input-group-append input-group-text',
13912                     html : this.after
13913                 });
13914             }
13915             
13916         };
13917         
13918       
13919         
13920         var ibwrap = inputblock;
13921         
13922         if(this.multiple){
13923             ibwrap = {
13924                 tag: 'ul',
13925                 cls: 'roo-select2-choices',
13926                 cn:[
13927                     {
13928                         tag: 'li',
13929                         cls: 'roo-select2-search-field',
13930                         cn: [
13931
13932                             inputblock
13933                         ]
13934                     }
13935                 ]
13936             };
13937                 
13938         }
13939         
13940         var combobox = {
13941             cls: 'roo-select2-container input-group',
13942             cn: [
13943                  {
13944                     tag: 'input',
13945                     type : 'hidden',
13946                     cls: 'form-hidden-field'
13947                 },
13948                 ibwrap
13949             ]
13950         };
13951         
13952         if(!this.multiple && this.showToggleBtn){
13953             
13954             var caret = {
13955                         tag: 'span',
13956                         cls: 'caret'
13957              };
13958             if (this.caret != false) {
13959                 caret = {
13960                      tag: 'i',
13961                      cls: 'fa fa-' + this.caret
13962                 };
13963                 
13964             }
13965             
13966             combobox.cn.push({
13967                 tag :'span',
13968                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13969                 cn : [
13970                     Roo.bootstrap.version == 3 ? caret : '',
13971                     {
13972                         tag: 'span',
13973                         cls: 'combobox-clear',
13974                         cn  : [
13975                             {
13976                                 tag : 'i',
13977                                 cls: 'icon-remove'
13978                             }
13979                         ]
13980                     }
13981                 ]
13982
13983             })
13984         }
13985         
13986         if(this.multiple){
13987             combobox.cls += ' roo-select2-container-multi';
13988         }
13989          var indicator = {
13990             tag : 'i',
13991             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13992             tooltip : 'This field is required'
13993         };
13994         if (Roo.bootstrap.version == 4) {
13995             indicator = {
13996                 tag : 'i',
13997                 style : 'display:none'
13998             };
13999         }
14000         
14001         
14002         if (align ==='left' && this.fieldLabel.length) {
14003             
14004             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14005
14006             cfg.cn = [
14007                 indicator,
14008                 {
14009                     tag: 'label',
14010                     'for' :  id,
14011                     cls : 'control-label',
14012                     html : this.fieldLabel
14013
14014                 },
14015                 {
14016                     cls : "", 
14017                     cn: [
14018                         combobox
14019                     ]
14020                 }
14021
14022             ];
14023             
14024             var labelCfg = cfg.cn[1];
14025             var contentCfg = cfg.cn[2];
14026             
14027             if(this.indicatorpos == 'right'){
14028                 cfg.cn = [
14029                     {
14030                         tag: 'label',
14031                         'for' :  id,
14032                         cls : 'control-label',
14033                         cn : [
14034                             {
14035                                 tag : 'span',
14036                                 html : this.fieldLabel
14037                             },
14038                             indicator
14039                         ]
14040                     },
14041                     {
14042                         cls : "", 
14043                         cn: [
14044                             combobox
14045                         ]
14046                     }
14047
14048                 ];
14049                 
14050                 labelCfg = cfg.cn[0];
14051                 contentCfg = cfg.cn[1];
14052             }
14053             
14054             if(this.labelWidth > 12){
14055                 labelCfg.style = "width: " + this.labelWidth + 'px';
14056             }
14057             
14058             if(this.labelWidth < 13 && this.labelmd == 0){
14059                 this.labelmd = this.labelWidth;
14060             }
14061             
14062             if(this.labellg > 0){
14063                 labelCfg.cls += ' col-lg-' + this.labellg;
14064                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14065             }
14066             
14067             if(this.labelmd > 0){
14068                 labelCfg.cls += ' col-md-' + this.labelmd;
14069                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14070             }
14071             
14072             if(this.labelsm > 0){
14073                 labelCfg.cls += ' col-sm-' + this.labelsm;
14074                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14075             }
14076             
14077             if(this.labelxs > 0){
14078                 labelCfg.cls += ' col-xs-' + this.labelxs;
14079                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14080             }
14081             
14082         } else if ( this.fieldLabel.length) {
14083 //                Roo.log(" label");
14084             cfg.cn = [
14085                 indicator,
14086                {
14087                    tag: 'label',
14088                    //cls : 'input-group-addon',
14089                    html : this.fieldLabel
14090
14091                },
14092
14093                combobox
14094
14095             ];
14096             
14097             if(this.indicatorpos == 'right'){
14098                 
14099                 cfg.cn = [
14100                     {
14101                        tag: 'label',
14102                        cn : [
14103                            {
14104                                tag : 'span',
14105                                html : this.fieldLabel
14106                            },
14107                            indicator
14108                        ]
14109
14110                     },
14111                     combobox
14112
14113                 ];
14114
14115             }
14116
14117         } else {
14118             
14119 //                Roo.log(" no label && no align");
14120                 cfg = combobox
14121                      
14122                 
14123         }
14124         
14125         var settings=this;
14126         ['xs','sm','md','lg'].map(function(size){
14127             if (settings[size]) {
14128                 cfg.cls += ' col-' + size + '-' + settings[size];
14129             }
14130         });
14131         
14132         return cfg;
14133         
14134     },
14135     
14136     
14137     
14138     // private
14139     onResize : function(w, h){
14140 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14141 //        if(typeof w == 'number'){
14142 //            var x = w - this.trigger.getWidth();
14143 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14144 //            this.trigger.setStyle('left', x+'px');
14145 //        }
14146     },
14147
14148     // private
14149     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14150
14151     // private
14152     getResizeEl : function(){
14153         return this.inputEl();
14154     },
14155
14156     // private
14157     getPositionEl : function(){
14158         return this.inputEl();
14159     },
14160
14161     // private
14162     alignErrorIcon : function(){
14163         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14164     },
14165
14166     // private
14167     initEvents : function(){
14168         
14169         this.createList();
14170         
14171         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14172         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14173         if(!this.multiple && this.showToggleBtn){
14174             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14175             if(this.hideTrigger){
14176                 this.trigger.setDisplayed(false);
14177             }
14178             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14179         }
14180         
14181         if(this.multiple){
14182             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14183         }
14184         
14185         if(this.removable && !this.editable && !this.tickable){
14186             var close = this.closeTriggerEl();
14187             
14188             if(close){
14189                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14190                 close.on('click', this.removeBtnClick, this, close);
14191             }
14192         }
14193         
14194         //this.trigger.addClassOnOver('x-form-trigger-over');
14195         //this.trigger.addClassOnClick('x-form-trigger-click');
14196         
14197         //if(!this.width){
14198         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14199         //}
14200     },
14201     
14202     closeTriggerEl : function()
14203     {
14204         var close = this.el.select('.roo-combo-removable-btn', true).first();
14205         return close ? close : false;
14206     },
14207     
14208     removeBtnClick : function(e, h, el)
14209     {
14210         e.preventDefault();
14211         
14212         if(this.fireEvent("remove", this) !== false){
14213             this.reset();
14214             this.fireEvent("afterremove", this)
14215         }
14216     },
14217     
14218     createList : function()
14219     {
14220         this.list = Roo.get(document.body).createChild({
14221             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14222             cls: 'typeahead typeahead-long dropdown-menu shadow',
14223             style: 'display:none'
14224         });
14225         
14226         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14227         
14228     },
14229
14230     // private
14231     initTrigger : function(){
14232        
14233     },
14234
14235     // private
14236     onDestroy : function(){
14237         if(this.trigger){
14238             this.trigger.removeAllListeners();
14239           //  this.trigger.remove();
14240         }
14241         //if(this.wrap){
14242         //    this.wrap.remove();
14243         //}
14244         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14245     },
14246
14247     // private
14248     onFocus : function(){
14249         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14250         /*
14251         if(!this.mimicing){
14252             this.wrap.addClass('x-trigger-wrap-focus');
14253             this.mimicing = true;
14254             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14255             if(this.monitorTab){
14256                 this.el.on("keydown", this.checkTab, this);
14257             }
14258         }
14259         */
14260     },
14261
14262     // private
14263     checkTab : function(e){
14264         if(e.getKey() == e.TAB){
14265             this.triggerBlur();
14266         }
14267     },
14268
14269     // private
14270     onBlur : function(){
14271         // do nothing
14272     },
14273
14274     // private
14275     mimicBlur : function(e, t){
14276         /*
14277         if(!this.wrap.contains(t) && this.validateBlur()){
14278             this.triggerBlur();
14279         }
14280         */
14281     },
14282
14283     // private
14284     triggerBlur : function(){
14285         this.mimicing = false;
14286         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14287         if(this.monitorTab){
14288             this.el.un("keydown", this.checkTab, this);
14289         }
14290         //this.wrap.removeClass('x-trigger-wrap-focus');
14291         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14292     },
14293
14294     // private
14295     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14296     validateBlur : function(e, t){
14297         return true;
14298     },
14299
14300     // private
14301     onDisable : function(){
14302         this.inputEl().dom.disabled = true;
14303         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14304         //if(this.wrap){
14305         //    this.wrap.addClass('x-item-disabled');
14306         //}
14307     },
14308
14309     // private
14310     onEnable : function(){
14311         this.inputEl().dom.disabled = false;
14312         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14313         //if(this.wrap){
14314         //    this.el.removeClass('x-item-disabled');
14315         //}
14316     },
14317
14318     // private
14319     onShow : function(){
14320         var ae = this.getActionEl();
14321         
14322         if(ae){
14323             ae.dom.style.display = '';
14324             ae.dom.style.visibility = 'visible';
14325         }
14326     },
14327
14328     // private
14329     
14330     onHide : function(){
14331         var ae = this.getActionEl();
14332         ae.dom.style.display = 'none';
14333     },
14334
14335     /**
14336      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14337      * by an implementing function.
14338      * @method
14339      * @param {EventObject} e
14340      */
14341     onTriggerClick : Roo.emptyFn
14342 });
14343  
14344 /*
14345 * Licence: LGPL
14346 */
14347
14348 /**
14349  * @class Roo.bootstrap.form.CardUploader
14350  * @extends Roo.bootstrap.Button
14351  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14352  * @cfg {Number} errorTimeout default 3000
14353  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14354  * @cfg {Array}  html The button text.
14355
14356  *
14357  * @constructor
14358  * Create a new CardUploader
14359  * @param {Object} config The config object
14360  */
14361
14362 Roo.bootstrap.form.CardUploader = function(config){
14363     
14364  
14365     
14366     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14367     
14368     
14369     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14370         return r.data.id
14371      });
14372     
14373      this.addEvents({
14374          // raw events
14375         /**
14376          * @event preview
14377          * When a image is clicked on - and needs to display a slideshow or similar..
14378          * @param {Roo.bootstrap.Card} this
14379          * @param {Object} The image information data 
14380          *
14381          */
14382         'preview' : true,
14383          /**
14384          * @event download
14385          * When a the download link is clicked
14386          * @param {Roo.bootstrap.Card} this
14387          * @param {Object} The image information data  contains 
14388          */
14389         'download' : true
14390         
14391     });
14392 };
14393  
14394 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14395     
14396      
14397     errorTimeout : 3000,
14398      
14399     images : false,
14400    
14401     fileCollection : false,
14402     allowBlank : true,
14403     
14404     getAutoCreate : function()
14405     {
14406         
14407         var cfg =  {
14408             cls :'form-group' ,
14409             cn : [
14410                
14411                 {
14412                     tag: 'label',
14413                    //cls : 'input-group-addon',
14414                     html : this.fieldLabel
14415
14416                 },
14417
14418                 {
14419                     tag: 'input',
14420                     type : 'hidden',
14421                     name : this.name,
14422                     value : this.value,
14423                     cls : 'd-none  form-control'
14424                 },
14425                 
14426                 {
14427                     tag: 'input',
14428                     multiple : 'multiple',
14429                     type : 'file',
14430                     cls : 'd-none  roo-card-upload-selector'
14431                 },
14432                 
14433                 {
14434                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14435                 },
14436                 {
14437                     cls : 'card-columns roo-card-uploader-container'
14438                 }
14439
14440             ]
14441         };
14442            
14443          
14444         return cfg;
14445     },
14446     
14447     getChildContainer : function() /// what children are added to.
14448     {
14449         return this.containerEl;
14450     },
14451    
14452     getButtonContainer : function() /// what children are added to.
14453     {
14454         return this.el.select(".roo-card-uploader-button-container").first();
14455     },
14456    
14457     initEvents : function()
14458     {
14459         
14460         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14461         
14462         var t = this;
14463         this.addxtype({
14464             xns: Roo.bootstrap,
14465
14466             xtype : 'Button',
14467             container_method : 'getButtonContainer' ,            
14468             html :  this.html, // fix changable?
14469             cls : 'w-100 ',
14470             listeners : {
14471                 'click' : function(btn, e) {
14472                     t.onClick(e);
14473                 }
14474             }
14475         });
14476         
14477         
14478         
14479         
14480         this.urlAPI = (window.createObjectURL && window) || 
14481                                 (window.URL && URL.revokeObjectURL && URL) || 
14482                                 (window.webkitURL && webkitURL);
14483                         
14484          
14485          
14486          
14487         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14488         
14489         this.selectorEl.on('change', this.onFileSelected, this);
14490         if (this.images) {
14491             var t = this;
14492             this.images.forEach(function(img) {
14493                 t.addCard(img)
14494             });
14495             this.images = false;
14496         }
14497         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14498          
14499        
14500     },
14501     
14502    
14503     onClick : function(e)
14504     {
14505         e.preventDefault();
14506          
14507         this.selectorEl.dom.click();
14508          
14509     },
14510     
14511     onFileSelected : function(e)
14512     {
14513         e.preventDefault();
14514         
14515         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14516             return;
14517         }
14518         
14519         Roo.each(this.selectorEl.dom.files, function(file){    
14520             this.addFile(file);
14521         }, this);
14522          
14523     },
14524     
14525       
14526     
14527       
14528     
14529     addFile : function(file)
14530     {
14531            
14532         if(typeof(file) === 'string'){
14533             throw "Add file by name?"; // should not happen
14534             return;
14535         }
14536         
14537         if(!file || !this.urlAPI){
14538             return;
14539         }
14540         
14541         // file;
14542         // file.type;
14543         
14544         var _this = this;
14545         
14546         
14547         var url = _this.urlAPI.createObjectURL( file);
14548            
14549         this.addCard({
14550             id : Roo.bootstrap.form.CardUploader.ID--,
14551             is_uploaded : false,
14552             src : url,
14553             srcfile : file,
14554             title : file.name,
14555             mimetype : file.type,
14556             preview : false,
14557             is_deleted : 0
14558         });
14559         
14560     },
14561     
14562     /**
14563      * addCard - add an Attachment to the uploader
14564      * @param data - the data about the image to upload
14565      *
14566      * {
14567           id : 123
14568           title : "Title of file",
14569           is_uploaded : false,
14570           src : "http://.....",
14571           srcfile : { the File upload object },
14572           mimetype : file.type,
14573           preview : false,
14574           is_deleted : 0
14575           .. any other data...
14576         }
14577      *
14578      * 
14579     */
14580     
14581     addCard : function (data)
14582     {
14583         // hidden input element?
14584         // if the file is not an image...
14585         //then we need to use something other that and header_image
14586         var t = this;
14587         //   remove.....
14588         var footer = [
14589             {
14590                 xns : Roo.bootstrap,
14591                 xtype : 'CardFooter',
14592                  items: [
14593                     {
14594                         xns : Roo.bootstrap,
14595                         xtype : 'Element',
14596                         cls : 'd-flex',
14597                         items : [
14598                             
14599                             {
14600                                 xns : Roo.bootstrap,
14601                                 xtype : 'Button',
14602                                 html : String.format("<small>{0}</small>", data.title),
14603                                 cls : 'col-10 text-left',
14604                                 size: 'sm',
14605                                 weight: 'link',
14606                                 fa : 'download',
14607                                 listeners : {
14608                                     click : function() {
14609                                      
14610                                         t.fireEvent( "download", t, data );
14611                                     }
14612                                 }
14613                             },
14614                           
14615                             {
14616                                 xns : Roo.bootstrap,
14617                                 xtype : 'Button',
14618                                 style: 'max-height: 28px; ',
14619                                 size : 'sm',
14620                                 weight: 'danger',
14621                                 cls : 'col-2',
14622                                 fa : 'times',
14623                                 listeners : {
14624                                     click : function() {
14625                                         t.removeCard(data.id)
14626                                     }
14627                                 }
14628                             }
14629                         ]
14630                     }
14631                     
14632                 ] 
14633             }
14634             
14635         ];
14636         
14637         var cn = this.addxtype(
14638             {
14639                  
14640                 xns : Roo.bootstrap,
14641                 xtype : 'Card',
14642                 closeable : true,
14643                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14644                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14645                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14646                 data : data,
14647                 html : false,
14648                  
14649                 items : footer,
14650                 initEvents : function() {
14651                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14652                     var card = this;
14653                     this.imgEl = this.el.select('.card-img-top').first();
14654                     if (this.imgEl) {
14655                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14656                         this.imgEl.set({ 'pointer' : 'cursor' });
14657                                   
14658                     }
14659                     this.getCardFooter().addClass('p-1');
14660                     
14661                   
14662                 }
14663                 
14664             }
14665         );
14666         // dont' really need ot update items.
14667         // this.items.push(cn);
14668         this.fileCollection.add(cn);
14669         
14670         if (!data.srcfile) {
14671             this.updateInput();
14672             return;
14673         }
14674             
14675         var _t = this;
14676         var reader = new FileReader();
14677         reader.addEventListener("load", function() {  
14678             data.srcdata =  reader.result;
14679             _t.updateInput();
14680         });
14681         reader.readAsDataURL(data.srcfile);
14682         
14683         
14684         
14685     },
14686     removeCard : function(id)
14687     {
14688         
14689         var card  = this.fileCollection.get(id);
14690         card.data.is_deleted = 1;
14691         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14692         //this.fileCollection.remove(card);
14693         //this.items = this.items.filter(function(e) { return e != card });
14694         // dont' really need ot update items.
14695         card.el.dom.parentNode.removeChild(card.el.dom);
14696         this.updateInput();
14697
14698         
14699     },
14700     reset: function()
14701     {
14702         this.fileCollection.each(function(card) {
14703             if (card.el.dom && card.el.dom.parentNode) {
14704                 card.el.dom.parentNode.removeChild(card.el.dom);
14705             }
14706         });
14707         this.fileCollection.clear();
14708         this.updateInput();
14709     },
14710     
14711     updateInput : function()
14712     {
14713          var data = [];
14714         this.fileCollection.each(function(e) {
14715             data.push(e.data);
14716             
14717         });
14718         this.inputEl().dom.value = JSON.stringify(data);
14719         
14720         
14721         
14722     }
14723     
14724     
14725 });
14726
14727
14728 Roo.bootstrap.form.CardUploader.ID = -1;/*
14729  * Based on:
14730  * Ext JS Library 1.1.1
14731  * Copyright(c) 2006-2007, Ext JS, LLC.
14732  *
14733  * Originally Released Under LGPL - original licence link has changed is not relivant.
14734  *
14735  * Fork - LGPL
14736  * <script type="text/javascript">
14737  */
14738
14739
14740 /**
14741  * @class Roo.data.SortTypes
14742  * @static
14743  * Defines the default sorting (casting?) comparison functions used when sorting data.
14744  */
14745 Roo.data.SortTypes = {
14746     /**
14747      * Default sort that does nothing
14748      * @param {Mixed} s The value being converted
14749      * @return {Mixed} The comparison value
14750      */
14751     none : function(s){
14752         return s;
14753     },
14754     
14755     /**
14756      * The regular expression used to strip tags
14757      * @type {RegExp}
14758      * @property
14759      */
14760     stripTagsRE : /<\/?[^>]+>/gi,
14761     
14762     /**
14763      * Strips all HTML tags to sort on text only
14764      * @param {Mixed} s The value being converted
14765      * @return {String} The comparison value
14766      */
14767     asText : function(s){
14768         return String(s).replace(this.stripTagsRE, "");
14769     },
14770     
14771     /**
14772      * Strips all HTML tags to sort on text only - Case insensitive
14773      * @param {Mixed} s The value being converted
14774      * @return {String} The comparison value
14775      */
14776     asUCText : function(s){
14777         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14778     },
14779     
14780     /**
14781      * Case insensitive string
14782      * @param {Mixed} s The value being converted
14783      * @return {String} The comparison value
14784      */
14785     asUCString : function(s) {
14786         return String(s).toUpperCase();
14787     },
14788     
14789     /**
14790      * Date sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asDate : function(s) {
14795         if(!s){
14796             return 0;
14797         }
14798         if(s instanceof Date){
14799             return s.getTime();
14800         }
14801         return Date.parse(String(s));
14802     },
14803     
14804     /**
14805      * Float sorting
14806      * @param {Mixed} s The value being converted
14807      * @return {Float} The comparison value
14808      */
14809     asFloat : function(s) {
14810         var val = parseFloat(String(s).replace(/,/g, ""));
14811         if(isNaN(val)) {
14812             val = 0;
14813         }
14814         return val;
14815     },
14816     
14817     /**
14818      * Integer sorting
14819      * @param {Mixed} s The value being converted
14820      * @return {Number} The comparison value
14821      */
14822     asInt : function(s) {
14823         var val = parseInt(String(s).replace(/,/g, ""));
14824         if(isNaN(val)) {
14825             val = 0;
14826         }
14827         return val;
14828     }
14829 };/*
14830  * Based on:
14831  * Ext JS Library 1.1.1
14832  * Copyright(c) 2006-2007, Ext JS, LLC.
14833  *
14834  * Originally Released Under LGPL - original licence link has changed is not relivant.
14835  *
14836  * Fork - LGPL
14837  * <script type="text/javascript">
14838  */
14839
14840 /**
14841 * @class Roo.data.Record
14842  * Instances of this class encapsulate both record <em>definition</em> information, and record
14843  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14844  * to access Records cached in an {@link Roo.data.Store} object.<br>
14845  * <p>
14846  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14847  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14848  * objects.<br>
14849  * <p>
14850  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14851  * @constructor
14852  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14853  * {@link #create}. The parameters are the same.
14854  * @param {Array} data An associative Array of data values keyed by the field name.
14855  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14856  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14857  * not specified an integer id is generated.
14858  */
14859 Roo.data.Record = function(data, id){
14860     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14861     this.data = data;
14862 };
14863
14864 /**
14865  * Generate a constructor for a specific record layout.
14866  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14867  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14868  * Each field definition object may contain the following properties: <ul>
14869  * <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,
14870  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14871  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14872  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14873  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14874  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14875  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14876  * this may be omitted.</p></li>
14877  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14878  * <ul><li>auto (Default, implies no conversion)</li>
14879  * <li>string</li>
14880  * <li>int</li>
14881  * <li>float</li>
14882  * <li>boolean</li>
14883  * <li>date</li></ul></p></li>
14884  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14885  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14886  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14887  * by the Reader into an object that will be stored in the Record. It is passed the
14888  * following parameters:<ul>
14889  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14890  * </ul></p></li>
14891  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14892  * </ul>
14893  * <br>usage:<br><pre><code>
14894 var TopicRecord = Roo.data.Record.create(
14895     {name: 'title', mapping: 'topic_title'},
14896     {name: 'author', mapping: 'username'},
14897     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14898     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14899     {name: 'lastPoster', mapping: 'user2'},
14900     {name: 'excerpt', mapping: 'post_text'}
14901 );
14902
14903 var myNewRecord = new TopicRecord({
14904     title: 'Do my job please',
14905     author: 'noobie',
14906     totalPosts: 1,
14907     lastPost: new Date(),
14908     lastPoster: 'Animal',
14909     excerpt: 'No way dude!'
14910 });
14911 myStore.add(myNewRecord);
14912 </code></pre>
14913  * @method create
14914  * @static
14915  */
14916 Roo.data.Record.create = function(o){
14917     var f = function(){
14918         f.superclass.constructor.apply(this, arguments);
14919     };
14920     Roo.extend(f, Roo.data.Record);
14921     var p = f.prototype;
14922     p.fields = new Roo.util.MixedCollection(false, function(field){
14923         return field.name;
14924     });
14925     for(var i = 0, len = o.length; i < len; i++){
14926         p.fields.add(new Roo.data.Field(o[i]));
14927     }
14928     f.getField = function(name){
14929         return p.fields.get(name);  
14930     };
14931     return f;
14932 };
14933
14934 Roo.data.Record.AUTO_ID = 1000;
14935 Roo.data.Record.EDIT = 'edit';
14936 Roo.data.Record.REJECT = 'reject';
14937 Roo.data.Record.COMMIT = 'commit';
14938
14939 Roo.data.Record.prototype = {
14940     /**
14941      * Readonly flag - true if this record has been modified.
14942      * @type Boolean
14943      */
14944     dirty : false,
14945     editing : false,
14946     error: null,
14947     modified: null,
14948
14949     // private
14950     join : function(store){
14951         this.store = store;
14952     },
14953
14954     /**
14955      * Set the named field to the specified value.
14956      * @param {String} name The name of the field to set.
14957      * @param {Object} value The value to set the field to.
14958      */
14959     set : function(name, value){
14960         if(this.data[name] == value){
14961             return;
14962         }
14963         this.dirty = true;
14964         if(!this.modified){
14965             this.modified = {};
14966         }
14967         if(typeof this.modified[name] == 'undefined'){
14968             this.modified[name] = this.data[name];
14969         }
14970         this.data[name] = value;
14971         if(!this.editing && this.store){
14972             this.store.afterEdit(this);
14973         }       
14974     },
14975
14976     /**
14977      * Get the value of the named field.
14978      * @param {String} name The name of the field to get the value of.
14979      * @return {Object} The value of the field.
14980      */
14981     get : function(name){
14982         return this.data[name]; 
14983     },
14984
14985     // private
14986     beginEdit : function(){
14987         this.editing = true;
14988         this.modified = {}; 
14989     },
14990
14991     // private
14992     cancelEdit : function(){
14993         this.editing = false;
14994         delete this.modified;
14995     },
14996
14997     // private
14998     endEdit : function(){
14999         this.editing = false;
15000         if(this.dirty && this.store){
15001             this.store.afterEdit(this);
15002         }
15003     },
15004
15005     /**
15006      * Usually called by the {@link Roo.data.Store} which owns the Record.
15007      * Rejects all changes made to the Record since either creation, or the last commit operation.
15008      * Modified fields are reverted to their original values.
15009      * <p>
15010      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15011      * of reject operations.
15012      */
15013     reject : function(){
15014         var m = this.modified;
15015         for(var n in m){
15016             if(typeof m[n] != "function"){
15017                 this.data[n] = m[n];
15018             }
15019         }
15020         this.dirty = false;
15021         delete this.modified;
15022         this.editing = false;
15023         if(this.store){
15024             this.store.afterReject(this);
15025         }
15026     },
15027
15028     /**
15029      * Usually called by the {@link Roo.data.Store} which owns the Record.
15030      * Commits all changes made to the Record since either creation, or the last commit operation.
15031      * <p>
15032      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15033      * of commit operations.
15034      */
15035     commit : function(){
15036         this.dirty = false;
15037         delete this.modified;
15038         this.editing = false;
15039         if(this.store){
15040             this.store.afterCommit(this);
15041         }
15042     },
15043
15044     // private
15045     hasError : function(){
15046         return this.error != null;
15047     },
15048
15049     // private
15050     clearError : function(){
15051         this.error = null;
15052     },
15053
15054     /**
15055      * Creates a copy of this record.
15056      * @param {String} id (optional) A new record id if you don't want to use this record's id
15057      * @return {Record}
15058      */
15059     copy : function(newId) {
15060         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15061     }
15062 };/*
15063  * Based on:
15064  * Ext JS Library 1.1.1
15065  * Copyright(c) 2006-2007, Ext JS, LLC.
15066  *
15067  * Originally Released Under LGPL - original licence link has changed is not relivant.
15068  *
15069  * Fork - LGPL
15070  * <script type="text/javascript">
15071  */
15072
15073
15074
15075 /**
15076  * @class Roo.data.Store
15077  * @extends Roo.util.Observable
15078  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15079  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15080  * <p>
15081  * 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
15082  * has no knowledge of the format of the data returned by the Proxy.<br>
15083  * <p>
15084  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15085  * instances from the data object. These records are cached and made available through accessor functions.
15086  * @constructor
15087  * Creates a new Store.
15088  * @param {Object} config A config object containing the objects needed for the Store to access data,
15089  * and read the data into Records.
15090  */
15091 Roo.data.Store = function(config){
15092     this.data = new Roo.util.MixedCollection(false);
15093     this.data.getKey = function(o){
15094         return o.id;
15095     };
15096     this.baseParams = {};
15097     // private
15098     this.paramNames = {
15099         "start" : "start",
15100         "limit" : "limit",
15101         "sort" : "sort",
15102         "dir" : "dir",
15103         "multisort" : "_multisort"
15104     };
15105
15106     if(config && config.data){
15107         this.inlineData = config.data;
15108         delete config.data;
15109     }
15110
15111     Roo.apply(this, config);
15112     
15113     if(this.reader){ // reader passed
15114         this.reader = Roo.factory(this.reader, Roo.data);
15115         this.reader.xmodule = this.xmodule || false;
15116         if(!this.recordType){
15117             this.recordType = this.reader.recordType;
15118         }
15119         if(this.reader.onMetaChange){
15120             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15121         }
15122     }
15123
15124     if(this.recordType){
15125         this.fields = this.recordType.prototype.fields;
15126     }
15127     this.modified = [];
15128
15129     this.addEvents({
15130         /**
15131          * @event datachanged
15132          * Fires when the data cache has changed, and a widget which is using this Store
15133          * as a Record cache should refresh its view.
15134          * @param {Store} this
15135          */
15136         datachanged : true,
15137         /**
15138          * @event metachange
15139          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15140          * @param {Store} this
15141          * @param {Object} meta The JSON metadata
15142          */
15143         metachange : true,
15144         /**
15145          * @event add
15146          * Fires when Records have been added to the Store
15147          * @param {Store} this
15148          * @param {Roo.data.Record[]} records The array of Records added
15149          * @param {Number} index The index at which the record(s) were added
15150          */
15151         add : true,
15152         /**
15153          * @event remove
15154          * Fires when a Record has been removed from the Store
15155          * @param {Store} this
15156          * @param {Roo.data.Record} record The Record that was removed
15157          * @param {Number} index The index at which the record was removed
15158          */
15159         remove : true,
15160         /**
15161          * @event update
15162          * Fires when a Record has been updated
15163          * @param {Store} this
15164          * @param {Roo.data.Record} record The Record that was updated
15165          * @param {String} operation The update operation being performed.  Value may be one of:
15166          * <pre><code>
15167  Roo.data.Record.EDIT
15168  Roo.data.Record.REJECT
15169  Roo.data.Record.COMMIT
15170          * </code></pre>
15171          */
15172         update : true,
15173         /**
15174          * @event clear
15175          * Fires when the data cache has been cleared.
15176          * @param {Store} this
15177          */
15178         clear : true,
15179         /**
15180          * @event beforeload
15181          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15182          * the load action will be canceled.
15183          * @param {Store} this
15184          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15185          */
15186         beforeload : true,
15187         /**
15188          * @event beforeloadadd
15189          * Fires after a new set of Records has been loaded.
15190          * @param {Store} this
15191          * @param {Roo.data.Record[]} records The Records that were loaded
15192          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15193          */
15194         beforeloadadd : true,
15195         /**
15196          * @event load
15197          * Fires after a new set of Records has been loaded, before they are added to the store.
15198          * @param {Store} this
15199          * @param {Roo.data.Record[]} records The Records that were loaded
15200          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15201          * @params {Object} return from reader
15202          */
15203         load : true,
15204         /**
15205          * @event loadexception
15206          * Fires if an exception occurs in the Proxy during loading.
15207          * Called with the signature of the Proxy's "loadexception" event.
15208          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15209          * 
15210          * @param {Proxy} 
15211          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15212          * @param {Object} load options 
15213          * @param {Object} jsonData from your request (normally this contains the Exception)
15214          */
15215         loadexception : true
15216     });
15217     
15218     if(this.proxy){
15219         this.proxy = Roo.factory(this.proxy, Roo.data);
15220         this.proxy.xmodule = this.xmodule || false;
15221         this.relayEvents(this.proxy,  ["loadexception"]);
15222     }
15223     this.sortToggle = {};
15224     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15225
15226     Roo.data.Store.superclass.constructor.call(this);
15227
15228     if(this.inlineData){
15229         this.loadData(this.inlineData);
15230         delete this.inlineData;
15231     }
15232 };
15233
15234 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15235      /**
15236     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15237     * without a remote query - used by combo/forms at present.
15238     */
15239     
15240     /**
15241     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15242     */
15243     /**
15244     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15245     */
15246     /**
15247     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15248     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15249     */
15250     /**
15251     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15252     * on any HTTP request
15253     */
15254     /**
15255     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15256     */
15257     /**
15258     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15259     */
15260     multiSort: false,
15261     /**
15262     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15263     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15264     */
15265     remoteSort : false,
15266
15267     /**
15268     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15269      * loaded or when a record is removed. (defaults to false).
15270     */
15271     pruneModifiedRecords : false,
15272
15273     // private
15274     lastOptions : null,
15275
15276     /**
15277      * Add Records to the Store and fires the add event.
15278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15279      */
15280     add : function(records){
15281         records = [].concat(records);
15282         for(var i = 0, len = records.length; i < len; i++){
15283             records[i].join(this);
15284         }
15285         var index = this.data.length;
15286         this.data.addAll(records);
15287         this.fireEvent("add", this, records, index);
15288     },
15289
15290     /**
15291      * Remove a Record from the Store and fires the remove event.
15292      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15293      */
15294     remove : function(record){
15295         var index = this.data.indexOf(record);
15296         this.data.removeAt(index);
15297  
15298         if(this.pruneModifiedRecords){
15299             this.modified.remove(record);
15300         }
15301         this.fireEvent("remove", this, record, index);
15302     },
15303
15304     /**
15305      * Remove all Records from the Store and fires the clear event.
15306      */
15307     removeAll : function(){
15308         this.data.clear();
15309         if(this.pruneModifiedRecords){
15310             this.modified = [];
15311         }
15312         this.fireEvent("clear", this);
15313     },
15314
15315     /**
15316      * Inserts Records to the Store at the given index and fires the add event.
15317      * @param {Number} index The start index at which to insert the passed Records.
15318      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15319      */
15320     insert : function(index, records){
15321         records = [].concat(records);
15322         for(var i = 0, len = records.length; i < len; i++){
15323             this.data.insert(index, records[i]);
15324             records[i].join(this);
15325         }
15326         this.fireEvent("add", this, records, index);
15327     },
15328
15329     /**
15330      * Get the index within the cache of the passed Record.
15331      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15332      * @return {Number} The index of the passed Record. Returns -1 if not found.
15333      */
15334     indexOf : function(record){
15335         return this.data.indexOf(record);
15336     },
15337
15338     /**
15339      * Get the index within the cache of the Record with the passed id.
15340      * @param {String} id The id of the Record to find.
15341      * @return {Number} The index of the Record. Returns -1 if not found.
15342      */
15343     indexOfId : function(id){
15344         return this.data.indexOfKey(id);
15345     },
15346
15347     /**
15348      * Get the Record with the specified id.
15349      * @param {String} id The id of the Record to find.
15350      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15351      */
15352     getById : function(id){
15353         return this.data.key(id);
15354     },
15355
15356     /**
15357      * Get the Record at the specified index.
15358      * @param {Number} index The index of the Record to find.
15359      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15360      */
15361     getAt : function(index){
15362         return this.data.itemAt(index);
15363     },
15364
15365     /**
15366      * Returns a range of Records between specified indices.
15367      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15368      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15369      * @return {Roo.data.Record[]} An array of Records
15370      */
15371     getRange : function(start, end){
15372         return this.data.getRange(start, end);
15373     },
15374
15375     // private
15376     storeOptions : function(o){
15377         o = Roo.apply({}, o);
15378         delete o.callback;
15379         delete o.scope;
15380         this.lastOptions = o;
15381     },
15382
15383     /**
15384      * Loads the Record cache from the configured Proxy using the configured Reader.
15385      * <p>
15386      * If using remote paging, then the first load call must specify the <em>start</em>
15387      * and <em>limit</em> properties in the options.params property to establish the initial
15388      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15389      * <p>
15390      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15391      * and this call will return before the new data has been loaded. Perform any post-processing
15392      * in a callback function, or in a "load" event handler.</strong>
15393      * <p>
15394      * @param {Object} options An object containing properties which control loading options:<ul>
15395      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15396      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15397      * <pre>
15398                 {
15399                     data : data,  // array of key=>value data like JsonReader
15400                     total : data.length,
15401                     success : true
15402                     
15403                 }
15404         </pre>
15405             }.</li>
15406      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15407      * passed the following arguments:<ul>
15408      * <li>r : Roo.data.Record[]</li>
15409      * <li>options: Options object from the load call</li>
15410      * <li>success: Boolean success indicator</li></ul></li>
15411      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15412      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15413      * </ul>
15414      */
15415     load : function(options){
15416         options = options || {};
15417         if(this.fireEvent("beforeload", this, options) !== false){
15418             this.storeOptions(options);
15419             var p = Roo.apply(options.params || {}, this.baseParams);
15420             // if meta was not loaded from remote source.. try requesting it.
15421             if (!this.reader.metaFromRemote) {
15422                 p._requestMeta = 1;
15423             }
15424             if(this.sortInfo && this.remoteSort){
15425                 var pn = this.paramNames;
15426                 p[pn["sort"]] = this.sortInfo.field;
15427                 p[pn["dir"]] = this.sortInfo.direction;
15428             }
15429             if (this.multiSort) {
15430                 var pn = this.paramNames;
15431                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15432             }
15433             
15434             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15435         }
15436     },
15437
15438     /**
15439      * Reloads the Record cache from the configured Proxy using the configured Reader and
15440      * the options from the last load operation performed.
15441      * @param {Object} options (optional) An object containing properties which may override the options
15442      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15443      * the most recently used options are reused).
15444      */
15445     reload : function(options){
15446         this.load(Roo.applyIf(options||{}, this.lastOptions));
15447     },
15448
15449     // private
15450     // Called as a callback by the Reader during a load operation.
15451     loadRecords : function(o, options, success){
15452          
15453         if(!o){
15454             if(success !== false){
15455                 this.fireEvent("load", this, [], options, o);
15456             }
15457             if(options.callback){
15458                 options.callback.call(options.scope || this, [], options, false);
15459             }
15460             return;
15461         }
15462         // if data returned failure - throw an exception.
15463         if (o.success === false) {
15464             // show a message if no listener is registered.
15465             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15466                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15467             }
15468             // loadmask wil be hooked into this..
15469             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15470             return;
15471         }
15472         var r = o.records, t = o.totalRecords || r.length;
15473         
15474         this.fireEvent("beforeloadadd", this, r, options, o);
15475         
15476         if(!options || options.add !== true){
15477             if(this.pruneModifiedRecords){
15478                 this.modified = [];
15479             }
15480             for(var i = 0, len = r.length; i < len; i++){
15481                 r[i].join(this);
15482             }
15483             if(this.snapshot){
15484                 this.data = this.snapshot;
15485                 delete this.snapshot;
15486             }
15487             this.data.clear();
15488             this.data.addAll(r);
15489             this.totalLength = t;
15490             this.applySort();
15491             this.fireEvent("datachanged", this);
15492         }else{
15493             this.totalLength = Math.max(t, this.data.length+r.length);
15494             this.add(r);
15495         }
15496         
15497         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15498                 
15499             var e = new Roo.data.Record({});
15500
15501             e.set(this.parent.displayField, this.parent.emptyTitle);
15502             e.set(this.parent.valueField, '');
15503
15504             this.insert(0, e);
15505         }
15506             
15507         this.fireEvent("load", this, r, options, o);
15508         if(options.callback){
15509             options.callback.call(options.scope || this, r, options, true);
15510         }
15511     },
15512
15513
15514     /**
15515      * Loads data from a passed data block. A Reader which understands the format of the data
15516      * must have been configured in the constructor.
15517      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15518      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15519      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15520      */
15521     loadData : function(o, append){
15522         var r = this.reader.readRecords(o);
15523         this.loadRecords(r, {add: append}, true);
15524     },
15525     
15526      /**
15527      * using 'cn' the nested child reader read the child array into it's child stores.
15528      * @param {Object} rec The record with a 'children array
15529      */
15530     loadDataFromChildren : function(rec)
15531     {
15532         this.loadData(this.reader.toLoadData(rec));
15533     },
15534     
15535
15536     /**
15537      * Gets the number of cached records.
15538      * <p>
15539      * <em>If using paging, this may not be the total size of the dataset. If the data object
15540      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15541      * the data set size</em>
15542      */
15543     getCount : function(){
15544         return this.data.length || 0;
15545     },
15546
15547     /**
15548      * Gets the total number of records in the dataset as returned by the server.
15549      * <p>
15550      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15551      * the dataset size</em>
15552      */
15553     getTotalCount : function(){
15554         return this.totalLength || 0;
15555     },
15556
15557     /**
15558      * Returns the sort state of the Store as an object with two properties:
15559      * <pre><code>
15560  field {String} The name of the field by which the Records are sorted
15561  direction {String} The sort order, "ASC" or "DESC"
15562      * </code></pre>
15563      */
15564     getSortState : function(){
15565         return this.sortInfo;
15566     },
15567
15568     // private
15569     applySort : function(){
15570         if(this.sortInfo && !this.remoteSort){
15571             var s = this.sortInfo, f = s.field;
15572             var st = this.fields.get(f).sortType;
15573             var fn = function(r1, r2){
15574                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15575                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15576             };
15577             this.data.sort(s.direction, fn);
15578             if(this.snapshot && this.snapshot != this.data){
15579                 this.snapshot.sort(s.direction, fn);
15580             }
15581         }
15582     },
15583
15584     /**
15585      * Sets the default sort column and order to be used by the next load operation.
15586      * @param {String} fieldName The name of the field to sort by.
15587      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15588      */
15589     setDefaultSort : function(field, dir){
15590         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15591     },
15592
15593     /**
15594      * Sort the Records.
15595      * If remote sorting is used, the sort is performed on the server, and the cache is
15596      * reloaded. If local sorting is used, the cache is sorted internally.
15597      * @param {String} fieldName The name of the field to sort by.
15598      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15599      */
15600     sort : function(fieldName, dir){
15601         var f = this.fields.get(fieldName);
15602         if(!dir){
15603             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15604             
15605             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15606                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15607             }else{
15608                 dir = f.sortDir;
15609             }
15610         }
15611         this.sortToggle[f.name] = dir;
15612         this.sortInfo = {field: f.name, direction: dir};
15613         if(!this.remoteSort){
15614             this.applySort();
15615             this.fireEvent("datachanged", this);
15616         }else{
15617             this.load(this.lastOptions);
15618         }
15619     },
15620
15621     /**
15622      * Calls the specified function for each of the Records in the cache.
15623      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15624      * Returning <em>false</em> aborts and exits the iteration.
15625      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15626      */
15627     each : function(fn, scope){
15628         this.data.each(fn, scope);
15629     },
15630
15631     /**
15632      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15633      * (e.g., during paging).
15634      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15635      */
15636     getModifiedRecords : function(){
15637         return this.modified;
15638     },
15639
15640     // private
15641     createFilterFn : function(property, value, anyMatch){
15642         if(!value.exec){ // not a regex
15643             value = String(value);
15644             if(value.length == 0){
15645                 return false;
15646             }
15647             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15648         }
15649         return function(r){
15650             return value.test(r.data[property]);
15651         };
15652     },
15653
15654     /**
15655      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15656      * @param {String} property A field on your records
15657      * @param {Number} start The record index to start at (defaults to 0)
15658      * @param {Number} end The last record index to include (defaults to length - 1)
15659      * @return {Number} The sum
15660      */
15661     sum : function(property, start, end){
15662         var rs = this.data.items, v = 0;
15663         start = start || 0;
15664         end = (end || end === 0) ? end : rs.length-1;
15665
15666         for(var i = start; i <= end; i++){
15667             v += (rs[i].data[property] || 0);
15668         }
15669         return v;
15670     },
15671
15672     /**
15673      * Filter the records by a specified property.
15674      * @param {String} field A field on your records
15675      * @param {String/RegExp} value Either a string that the field
15676      * should start with or a RegExp to test against the field
15677      * @param {Boolean} anyMatch True to match any part not just the beginning
15678      */
15679     filter : function(property, value, anyMatch){
15680         var fn = this.createFilterFn(property, value, anyMatch);
15681         return fn ? this.filterBy(fn) : this.clearFilter();
15682     },
15683
15684     /**
15685      * Filter by a function. The specified function will be called with each
15686      * record in this data source. If the function returns true the record is included,
15687      * otherwise it is filtered.
15688      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15689      * @param {Object} scope (optional) The scope of the function (defaults to this)
15690      */
15691     filterBy : function(fn, scope){
15692         this.snapshot = this.snapshot || this.data;
15693         this.data = this.queryBy(fn, scope||this);
15694         this.fireEvent("datachanged", this);
15695     },
15696
15697     /**
15698      * Query the records by a specified property.
15699      * @param {String} field A field on your records
15700      * @param {String/RegExp} value Either a string that the field
15701      * should start with or a RegExp to test against the field
15702      * @param {Boolean} anyMatch True to match any part not just the beginning
15703      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15704      */
15705     query : function(property, value, anyMatch){
15706         var fn = this.createFilterFn(property, value, anyMatch);
15707         return fn ? this.queryBy(fn) : this.data.clone();
15708     },
15709
15710     /**
15711      * Query by a function. The specified function will be called with each
15712      * record in this data source. If the function returns true the record is included
15713      * in the results.
15714      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15715      * @param {Object} scope (optional) The scope of the function (defaults to this)
15716       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15717      **/
15718     queryBy : function(fn, scope){
15719         var data = this.snapshot || this.data;
15720         return data.filterBy(fn, scope||this);
15721     },
15722
15723     /**
15724      * Collects unique values for a particular dataIndex from this store.
15725      * @param {String} dataIndex The property to collect
15726      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15727      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15728      * @return {Array} An array of the unique values
15729      **/
15730     collect : function(dataIndex, allowNull, bypassFilter){
15731         var d = (bypassFilter === true && this.snapshot) ?
15732                 this.snapshot.items : this.data.items;
15733         var v, sv, r = [], l = {};
15734         for(var i = 0, len = d.length; i < len; i++){
15735             v = d[i].data[dataIndex];
15736             sv = String(v);
15737             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15738                 l[sv] = true;
15739                 r[r.length] = v;
15740             }
15741         }
15742         return r;
15743     },
15744
15745     /**
15746      * Revert to a view of the Record cache with no filtering applied.
15747      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15748      */
15749     clearFilter : function(suppressEvent){
15750         if(this.snapshot && this.snapshot != this.data){
15751             this.data = this.snapshot;
15752             delete this.snapshot;
15753             if(suppressEvent !== true){
15754                 this.fireEvent("datachanged", this);
15755             }
15756         }
15757     },
15758
15759     // private
15760     afterEdit : function(record){
15761         if(this.modified.indexOf(record) == -1){
15762             this.modified.push(record);
15763         }
15764         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15765     },
15766     
15767     // private
15768     afterReject : function(record){
15769         this.modified.remove(record);
15770         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15771     },
15772
15773     // private
15774     afterCommit : function(record){
15775         this.modified.remove(record);
15776         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15777     },
15778
15779     /**
15780      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15781      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15782      */
15783     commitChanges : function(){
15784         var m = this.modified.slice(0);
15785         this.modified = [];
15786         for(var i = 0, len = m.length; i < len; i++){
15787             m[i].commit();
15788         }
15789     },
15790
15791     /**
15792      * Cancel outstanding changes on all changed records.
15793      */
15794     rejectChanges : function(){
15795         var m = this.modified.slice(0);
15796         this.modified = [];
15797         for(var i = 0, len = m.length; i < len; i++){
15798             m[i].reject();
15799         }
15800     },
15801
15802     onMetaChange : function(meta, rtype, o){
15803         this.recordType = rtype;
15804         this.fields = rtype.prototype.fields;
15805         delete this.snapshot;
15806         this.sortInfo = meta.sortInfo || this.sortInfo;
15807         this.modified = [];
15808         this.fireEvent('metachange', this, this.reader.meta);
15809     },
15810     
15811     moveIndex : function(data, type)
15812     {
15813         var index = this.indexOf(data);
15814         
15815         var newIndex = index + type;
15816         
15817         this.remove(data);
15818         
15819         this.insert(newIndex, data);
15820         
15821     }
15822 });/*
15823  * Based on:
15824  * Ext JS Library 1.1.1
15825  * Copyright(c) 2006-2007, Ext JS, LLC.
15826  *
15827  * Originally Released Under LGPL - original licence link has changed is not relivant.
15828  *
15829  * Fork - LGPL
15830  * <script type="text/javascript">
15831  */
15832
15833 /**
15834  * @class Roo.data.SimpleStore
15835  * @extends Roo.data.Store
15836  * Small helper class to make creating Stores from Array data easier.
15837  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15838  * @cfg {Array} fields An array of field definition objects, or field name strings.
15839  * @cfg {Object} an existing reader (eg. copied from another store)
15840  * @cfg {Array} data The multi-dimensional array of data
15841  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15842  * @cfg {Roo.data.Reader} reader  [not-required] 
15843  * @constructor
15844  * @param {Object} config
15845  */
15846 Roo.data.SimpleStore = function(config)
15847 {
15848     Roo.data.SimpleStore.superclass.constructor.call(this, {
15849         isLocal : true,
15850         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15851                 id: config.id
15852             },
15853             Roo.data.Record.create(config.fields)
15854         ),
15855         proxy : new Roo.data.MemoryProxy(config.data)
15856     });
15857     this.load();
15858 };
15859 Roo.extend(Roo.data.SimpleStore, 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 /**
15872  * @extends Roo.data.Store
15873  * @class Roo.data.JsonStore
15874  * Small helper class to make creating Stores for JSON data easier. <br/>
15875 <pre><code>
15876 var store = new Roo.data.JsonStore({
15877     url: 'get-images.php',
15878     root: 'images',
15879     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15880 });
15881 </code></pre>
15882  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15883  * JsonReader and HttpProxy (unless inline data is provided).</b>
15884  * @cfg {Array} fields An array of field definition objects, or field name strings.
15885  * @constructor
15886  * @param {Object} config
15887  */
15888 Roo.data.JsonStore = function(c){
15889     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15890         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15891         reader: new Roo.data.JsonReader(c, c.fields)
15892     }));
15893 };
15894 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15895  * Based on:
15896  * Ext JS Library 1.1.1
15897  * Copyright(c) 2006-2007, Ext JS, LLC.
15898  *
15899  * Originally Released Under LGPL - original licence link has changed is not relivant.
15900  *
15901  * Fork - LGPL
15902  * <script type="text/javascript">
15903  */
15904
15905  
15906 Roo.data.Field = function(config){
15907     if(typeof config == "string"){
15908         config = {name: config};
15909     }
15910     Roo.apply(this, config);
15911     
15912     if(!this.type){
15913         this.type = "auto";
15914     }
15915     
15916     var st = Roo.data.SortTypes;
15917     // named sortTypes are supported, here we look them up
15918     if(typeof this.sortType == "string"){
15919         this.sortType = st[this.sortType];
15920     }
15921     
15922     // set default sortType for strings and dates
15923     if(!this.sortType){
15924         switch(this.type){
15925             case "string":
15926                 this.sortType = st.asUCString;
15927                 break;
15928             case "date":
15929                 this.sortType = st.asDate;
15930                 break;
15931             default:
15932                 this.sortType = st.none;
15933         }
15934     }
15935
15936     // define once
15937     var stripRe = /[\$,%]/g;
15938
15939     // prebuilt conversion function for this field, instead of
15940     // switching every time we're reading a value
15941     if(!this.convert){
15942         var cv, dateFormat = this.dateFormat;
15943         switch(this.type){
15944             case "":
15945             case "auto":
15946             case undefined:
15947                 cv = function(v){ return v; };
15948                 break;
15949             case "string":
15950                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15951                 break;
15952             case "int":
15953                 cv = function(v){
15954                     return v !== undefined && v !== null && v !== '' ?
15955                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15956                     };
15957                 break;
15958             case "float":
15959                 cv = function(v){
15960                     return v !== undefined && v !== null && v !== '' ?
15961                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15962                     };
15963                 break;
15964             case "bool":
15965             case "boolean":
15966                 cv = function(v){ return v === true || v === "true" || v == 1; };
15967                 break;
15968             case "date":
15969                 cv = function(v){
15970                     if(!v){
15971                         return '';
15972                     }
15973                     if(v instanceof Date){
15974                         return v;
15975                     }
15976                     if(dateFormat){
15977                         if(dateFormat == "timestamp"){
15978                             return new Date(v*1000);
15979                         }
15980                         return Date.parseDate(v, dateFormat);
15981                     }
15982                     var parsed = Date.parse(v);
15983                     return parsed ? new Date(parsed) : null;
15984                 };
15985              break;
15986             
15987         }
15988         this.convert = cv;
15989     }
15990 };
15991
15992 Roo.data.Field.prototype = {
15993     dateFormat: null,
15994     defaultValue: "",
15995     mapping: null,
15996     sortType : null,
15997     sortDir : "ASC"
15998 };/*
15999  * Based on:
16000  * Ext JS Library 1.1.1
16001  * Copyright(c) 2006-2007, Ext JS, LLC.
16002  *
16003  * Originally Released Under LGPL - original licence link has changed is not relivant.
16004  *
16005  * Fork - LGPL
16006  * <script type="text/javascript">
16007  */
16008  
16009 // Base class for reading structured data from a data source.  This class is intended to be
16010 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16011
16012 /**
16013  * @class Roo.data.DataReader
16014  * @abstract
16015  * Base class for reading structured data from a data source.  This class is intended to be
16016  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16017  */
16018
16019 Roo.data.DataReader = function(meta, recordType){
16020     
16021     this.meta = meta;
16022     
16023     this.recordType = recordType instanceof Array ? 
16024         Roo.data.Record.create(recordType) : recordType;
16025 };
16026
16027 Roo.data.DataReader.prototype = {
16028     
16029     
16030     readerType : 'Data',
16031      /**
16032      * Create an empty record
16033      * @param {Object} data (optional) - overlay some values
16034      * @return {Roo.data.Record} record created.
16035      */
16036     newRow :  function(d) {
16037         var da =  {};
16038         this.recordType.prototype.fields.each(function(c) {
16039             switch( c.type) {
16040                 case 'int' : da[c.name] = 0; break;
16041                 case 'date' : da[c.name] = new Date(); break;
16042                 case 'float' : da[c.name] = 0.0; break;
16043                 case 'boolean' : da[c.name] = false; break;
16044                 default : da[c.name] = ""; break;
16045             }
16046             
16047         });
16048         return new this.recordType(Roo.apply(da, d));
16049     }
16050     
16051     
16052 };/*
16053  * Based on:
16054  * Ext JS Library 1.1.1
16055  * Copyright(c) 2006-2007, Ext JS, LLC.
16056  *
16057  * Originally Released Under LGPL - original licence link has changed is not relivant.
16058  *
16059  * Fork - LGPL
16060  * <script type="text/javascript">
16061  */
16062
16063 /**
16064  * @class Roo.data.DataProxy
16065  * @extends Roo.util.Observable
16066  * @abstract
16067  * This class is an abstract base class for implementations which provide retrieval of
16068  * unformatted data objects.<br>
16069  * <p>
16070  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16071  * (of the appropriate type which knows how to parse the data object) to provide a block of
16072  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16073  * <p>
16074  * Custom implementations must implement the load method as described in
16075  * {@link Roo.data.HttpProxy#load}.
16076  */
16077 Roo.data.DataProxy = function(){
16078     this.addEvents({
16079         /**
16080          * @event beforeload
16081          * Fires before a network request is made to retrieve a data object.
16082          * @param {Object} This DataProxy object.
16083          * @param {Object} params The params parameter to the load function.
16084          */
16085         beforeload : true,
16086         /**
16087          * @event load
16088          * Fires before the load method's callback is called.
16089          * @param {Object} This DataProxy object.
16090          * @param {Object} o The data object.
16091          * @param {Object} arg The callback argument object passed to the load function.
16092          */
16093         load : true,
16094         /**
16095          * @event loadexception
16096          * Fires if an Exception occurs during data retrieval.
16097          * @param {Object} This DataProxy object.
16098          * @param {Object} o The data object.
16099          * @param {Object} arg The callback argument object passed to the load function.
16100          * @param {Object} e The Exception.
16101          */
16102         loadexception : true
16103     });
16104     Roo.data.DataProxy.superclass.constructor.call(this);
16105 };
16106
16107 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16108
16109     /**
16110      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16111      */
16112 /*
16113  * Based on:
16114  * Ext JS Library 1.1.1
16115  * Copyright(c) 2006-2007, Ext JS, LLC.
16116  *
16117  * Originally Released Under LGPL - original licence link has changed is not relivant.
16118  *
16119  * Fork - LGPL
16120  * <script type="text/javascript">
16121  */
16122 /**
16123  * @class Roo.data.MemoryProxy
16124  * @extends Roo.data.DataProxy
16125  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16126  * to the Reader when its load method is called.
16127  * @constructor
16128  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16129  */
16130 Roo.data.MemoryProxy = function(config){
16131     var data = config;
16132     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16133         data = config.data;
16134     }
16135     Roo.data.MemoryProxy.superclass.constructor.call(this);
16136     this.data = data;
16137 };
16138
16139 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16140     
16141     /**
16142      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16143      */
16144     /**
16145      * Load data from the requested source (in this case an in-memory
16146      * data object passed to the constructor), read the data object into
16147      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16148      * process that block using the passed callback.
16149      * @param {Object} params This parameter is not used by the MemoryProxy class.
16150      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16151      * object into a block of Roo.data.Records.
16152      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16153      * The function must be passed <ul>
16154      * <li>The Record block object</li>
16155      * <li>The "arg" argument from the load function</li>
16156      * <li>A boolean success indicator</li>
16157      * </ul>
16158      * @param {Object} scope The scope in which to call the callback
16159      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16160      */
16161     load : function(params, reader, callback, scope, arg){
16162         params = params || {};
16163         var result;
16164         try {
16165             result = reader.readRecords(params.data ? params.data :this.data);
16166         }catch(e){
16167             this.fireEvent("loadexception", this, arg, null, e);
16168             callback.call(scope, null, arg, false);
16169             return;
16170         }
16171         callback.call(scope, result, arg, true);
16172     },
16173     
16174     // private
16175     update : function(params, records){
16176         
16177     }
16178 });/*
16179  * Based on:
16180  * Ext JS Library 1.1.1
16181  * Copyright(c) 2006-2007, Ext JS, LLC.
16182  *
16183  * Originally Released Under LGPL - original licence link has changed is not relivant.
16184  *
16185  * Fork - LGPL
16186  * <script type="text/javascript">
16187  */
16188 /**
16189  * @class Roo.data.HttpProxy
16190  * @extends Roo.data.DataProxy
16191  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16192  * configured to reference a certain URL.<br><br>
16193  * <p>
16194  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16195  * from which the running page was served.<br><br>
16196  * <p>
16197  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16198  * <p>
16199  * Be aware that to enable the browser to parse an XML document, the server must set
16200  * the Content-Type header in the HTTP response to "text/xml".
16201  * @constructor
16202  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16203  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16204  * will be used to make the request.
16205  */
16206 Roo.data.HttpProxy = function(conn){
16207     Roo.data.HttpProxy.superclass.constructor.call(this);
16208     // is conn a conn config or a real conn?
16209     this.conn = conn;
16210     this.useAjax = !conn || !conn.events;
16211   
16212 };
16213
16214 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16215     // thse are take from connection...
16216     
16217     /**
16218      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16219      */
16220     /**
16221      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16222      * extra parameters to each request made by this object. (defaults to undefined)
16223      */
16224     /**
16225      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16226      *  to each request made by this object. (defaults to undefined)
16227      */
16228     /**
16229      * @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)
16230      */
16231     /**
16232      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16233      */
16234      /**
16235      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16236      * @type Boolean
16237      */
16238   
16239
16240     /**
16241      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16242      * @type Boolean
16243      */
16244     /**
16245      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16246      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16247      * a finer-grained basis than the DataProxy events.
16248      */
16249     getConnection : function(){
16250         return this.useAjax ? Roo.Ajax : this.conn;
16251     },
16252
16253     /**
16254      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16255      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16256      * process that block using the passed callback.
16257      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16258      * for the request to the remote server.
16259      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16260      * object into a block of Roo.data.Records.
16261      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16262      * The function must be passed <ul>
16263      * <li>The Record block object</li>
16264      * <li>The "arg" argument from the load function</li>
16265      * <li>A boolean success indicator</li>
16266      * </ul>
16267      * @param {Object} scope The scope in which to call the callback
16268      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16269      */
16270     load : function(params, reader, callback, scope, arg){
16271         if(this.fireEvent("beforeload", this, params) !== false){
16272             var  o = {
16273                 params : params || {},
16274                 request: {
16275                     callback : callback,
16276                     scope : scope,
16277                     arg : arg
16278                 },
16279                 reader: reader,
16280                 callback : this.loadResponse,
16281                 scope: this
16282             };
16283             if(this.useAjax){
16284                 Roo.applyIf(o, this.conn);
16285                 if(this.activeRequest){
16286                     Roo.Ajax.abort(this.activeRequest);
16287                 }
16288                 this.activeRequest = Roo.Ajax.request(o);
16289             }else{
16290                 this.conn.request(o);
16291             }
16292         }else{
16293             callback.call(scope||this, null, arg, false);
16294         }
16295     },
16296
16297     // private
16298     loadResponse : function(o, success, response){
16299         delete this.activeRequest;
16300         if(!success){
16301             this.fireEvent("loadexception", this, o, response);
16302             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16303             return;
16304         }
16305         var result;
16306         try {
16307             result = o.reader.read(response);
16308         }catch(e){
16309             o.success = false;
16310             o.raw = { errorMsg : response.responseText };
16311             this.fireEvent("loadexception", this, o, response, e);
16312             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16313             return;
16314         }
16315         
16316         this.fireEvent("load", this, o, o.request.arg);
16317         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16318     },
16319
16320     // private
16321     update : function(dataSet){
16322
16323     },
16324
16325     // private
16326     updateResponse : function(dataSet){
16327
16328     }
16329 });/*
16330  * Based on:
16331  * Ext JS Library 1.1.1
16332  * Copyright(c) 2006-2007, Ext JS, LLC.
16333  *
16334  * Originally Released Under LGPL - original licence link has changed is not relivant.
16335  *
16336  * Fork - LGPL
16337  * <script type="text/javascript">
16338  */
16339
16340 /**
16341  * @class Roo.data.ScriptTagProxy
16342  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16343  * other than the originating domain of the running page.<br><br>
16344  * <p>
16345  * <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
16346  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16347  * <p>
16348  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16349  * source code that is used as the source inside a &lt;script> tag.<br><br>
16350  * <p>
16351  * In order for the browser to process the returned data, the server must wrap the data object
16352  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16353  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16354  * depending on whether the callback name was passed:
16355  * <p>
16356  * <pre><code>
16357 boolean scriptTag = false;
16358 String cb = request.getParameter("callback");
16359 if (cb != null) {
16360     scriptTag = true;
16361     response.setContentType("text/javascript");
16362 } else {
16363     response.setContentType("application/x-json");
16364 }
16365 Writer out = response.getWriter();
16366 if (scriptTag) {
16367     out.write(cb + "(");
16368 }
16369 out.print(dataBlock.toJsonString());
16370 if (scriptTag) {
16371     out.write(");");
16372 }
16373 </pre></code>
16374  *
16375  * @constructor
16376  * @param {Object} config A configuration object.
16377  */
16378 Roo.data.ScriptTagProxy = function(config){
16379     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16380     Roo.apply(this, config);
16381     this.head = document.getElementsByTagName("head")[0];
16382 };
16383
16384 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16385
16386 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16387     /**
16388      * @cfg {String} url The URL from which to request the data object.
16389      */
16390     /**
16391      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16392      */
16393     timeout : 30000,
16394     /**
16395      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16396      * the server the name of the callback function set up by the load call to process the returned data object.
16397      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16398      * javascript output which calls this named function passing the data object as its only parameter.
16399      */
16400     callbackParam : "callback",
16401     /**
16402      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16403      * name to the request.
16404      */
16405     nocache : true,
16406
16407     /**
16408      * Load data from the configured URL, read the data object into
16409      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16410      * process that block using the passed callback.
16411      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16412      * for the request to the remote server.
16413      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16414      * object into a block of Roo.data.Records.
16415      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16416      * The function must be passed <ul>
16417      * <li>The Record block object</li>
16418      * <li>The "arg" argument from the load function</li>
16419      * <li>A boolean success indicator</li>
16420      * </ul>
16421      * @param {Object} scope The scope in which to call the callback
16422      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16423      */
16424     load : function(params, reader, callback, scope, arg){
16425         if(this.fireEvent("beforeload", this, params) !== false){
16426
16427             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16428
16429             var url = this.url;
16430             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16431             if(this.nocache){
16432                 url += "&_dc=" + (new Date().getTime());
16433             }
16434             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16435             var trans = {
16436                 id : transId,
16437                 cb : "stcCallback"+transId,
16438                 scriptId : "stcScript"+transId,
16439                 params : params,
16440                 arg : arg,
16441                 url : url,
16442                 callback : callback,
16443                 scope : scope,
16444                 reader : reader
16445             };
16446             var conn = this;
16447
16448             window[trans.cb] = function(o){
16449                 conn.handleResponse(o, trans);
16450             };
16451
16452             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16453
16454             if(this.autoAbort !== false){
16455                 this.abort();
16456             }
16457
16458             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16459
16460             var script = document.createElement("script");
16461             script.setAttribute("src", url);
16462             script.setAttribute("type", "text/javascript");
16463             script.setAttribute("id", trans.scriptId);
16464             this.head.appendChild(script);
16465
16466             this.trans = trans;
16467         }else{
16468             callback.call(scope||this, null, arg, false);
16469         }
16470     },
16471
16472     // private
16473     isLoading : function(){
16474         return this.trans ? true : false;
16475     },
16476
16477     /**
16478      * Abort the current server request.
16479      */
16480     abort : function(){
16481         if(this.isLoading()){
16482             this.destroyTrans(this.trans);
16483         }
16484     },
16485
16486     // private
16487     destroyTrans : function(trans, isLoaded){
16488         this.head.removeChild(document.getElementById(trans.scriptId));
16489         clearTimeout(trans.timeoutId);
16490         if(isLoaded){
16491             window[trans.cb] = undefined;
16492             try{
16493                 delete window[trans.cb];
16494             }catch(e){}
16495         }else{
16496             // if hasn't been loaded, wait for load to remove it to prevent script error
16497             window[trans.cb] = function(){
16498                 window[trans.cb] = undefined;
16499                 try{
16500                     delete window[trans.cb];
16501                 }catch(e){}
16502             };
16503         }
16504     },
16505
16506     // private
16507     handleResponse : function(o, trans){
16508         this.trans = false;
16509         this.destroyTrans(trans, true);
16510         var result;
16511         try {
16512             result = trans.reader.readRecords(o);
16513         }catch(e){
16514             this.fireEvent("loadexception", this, o, trans.arg, e);
16515             trans.callback.call(trans.scope||window, null, trans.arg, false);
16516             return;
16517         }
16518         this.fireEvent("load", this, o, trans.arg);
16519         trans.callback.call(trans.scope||window, result, trans.arg, true);
16520     },
16521
16522     // private
16523     handleFailure : function(trans){
16524         this.trans = false;
16525         this.destroyTrans(trans, false);
16526         this.fireEvent("loadexception", this, null, trans.arg);
16527         trans.callback.call(trans.scope||window, null, trans.arg, false);
16528     }
16529 });/*
16530  * Based on:
16531  * Ext JS Library 1.1.1
16532  * Copyright(c) 2006-2007, Ext JS, LLC.
16533  *
16534  * Originally Released Under LGPL - original licence link has changed is not relivant.
16535  *
16536  * Fork - LGPL
16537  * <script type="text/javascript">
16538  */
16539
16540 /**
16541  * @class Roo.data.JsonReader
16542  * @extends Roo.data.DataReader
16543  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16544  * based on mappings in a provided Roo.data.Record constructor.
16545  * 
16546  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16547  * in the reply previously. 
16548  * 
16549  * <p>
16550  * Example code:
16551  * <pre><code>
16552 var RecordDef = Roo.data.Record.create([
16553     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16554     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16555 ]);
16556 var myReader = new Roo.data.JsonReader({
16557     totalProperty: "results",    // The property which contains the total dataset size (optional)
16558     root: "rows",                // The property which contains an Array of row objects
16559     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16560 }, RecordDef);
16561 </code></pre>
16562  * <p>
16563  * This would consume a JSON file like this:
16564  * <pre><code>
16565 { 'results': 2, 'rows': [
16566     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16567     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16568 }
16569 </code></pre>
16570  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16571  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16572  * paged from the remote server.
16573  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16574  * @cfg {String} root name of the property which contains the Array of row objects.
16575  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16576  * @cfg {Array} fields Array of field definition objects
16577  * @constructor
16578  * Create a new JsonReader
16579  * @param {Object} meta Metadata configuration options
16580  * @param {Object} recordType Either an Array of field definition objects,
16581  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16582  */
16583 Roo.data.JsonReader = function(meta, recordType){
16584     
16585     meta = meta || {};
16586     // set some defaults:
16587     Roo.applyIf(meta, {
16588         totalProperty: 'total',
16589         successProperty : 'success',
16590         root : 'data',
16591         id : 'id'
16592     });
16593     
16594     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16595 };
16596 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16597     
16598     readerType : 'Json',
16599     
16600     /**
16601      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16602      * Used by Store query builder to append _requestMeta to params.
16603      * 
16604      */
16605     metaFromRemote : false,
16606     /**
16607      * This method is only used by a DataProxy which has retrieved data from a remote server.
16608      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16609      * @return {Object} data A data block which is used by an Roo.data.Store object as
16610      * a cache of Roo.data.Records.
16611      */
16612     read : function(response){
16613         var json = response.responseText;
16614        
16615         var o = /* eval:var:o */ eval("("+json+")");
16616         if(!o) {
16617             throw {message: "JsonReader.read: Json object not found"};
16618         }
16619         
16620         if(o.metaData){
16621             
16622             delete this.ef;
16623             this.metaFromRemote = true;
16624             this.meta = o.metaData;
16625             this.recordType = Roo.data.Record.create(o.metaData.fields);
16626             this.onMetaChange(this.meta, this.recordType, o);
16627         }
16628         return this.readRecords(o);
16629     },
16630
16631     // private function a store will implement
16632     onMetaChange : function(meta, recordType, o){
16633
16634     },
16635
16636     /**
16637          * @ignore
16638          */
16639     simpleAccess: function(obj, subsc) {
16640         return obj[subsc];
16641     },
16642
16643         /**
16644          * @ignore
16645          */
16646     getJsonAccessor: function(){
16647         var re = /[\[\.]/;
16648         return function(expr) {
16649             try {
16650                 return(re.test(expr))
16651                     ? new Function("obj", "return obj." + expr)
16652                     : function(obj){
16653                         return obj[expr];
16654                     };
16655             } catch(e){}
16656             return Roo.emptyFn;
16657         };
16658     }(),
16659
16660     /**
16661      * Create a data block containing Roo.data.Records from an XML document.
16662      * @param {Object} o An object which contains an Array of row objects in the property specified
16663      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16664      * which contains the total size of the dataset.
16665      * @return {Object} data A data block which is used by an Roo.data.Store object as
16666      * a cache of Roo.data.Records.
16667      */
16668     readRecords : function(o){
16669         /**
16670          * After any data loads, the raw JSON data is available for further custom processing.
16671          * @type Object
16672          */
16673         this.o = o;
16674         var s = this.meta, Record = this.recordType,
16675             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16676
16677 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16678         if (!this.ef) {
16679             if(s.totalProperty) {
16680                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16681                 }
16682                 if(s.successProperty) {
16683                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16684                 }
16685                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16686                 if (s.id) {
16687                         var g = this.getJsonAccessor(s.id);
16688                         this.getId = function(rec) {
16689                                 var r = g(rec);  
16690                                 return (r === undefined || r === "") ? null : r;
16691                         };
16692                 } else {
16693                         this.getId = function(){return null;};
16694                 }
16695             this.ef = [];
16696             for(var jj = 0; jj < fl; jj++){
16697                 f = fi[jj];
16698                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16699                 this.ef[jj] = this.getJsonAccessor(map);
16700             }
16701         }
16702
16703         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16704         if(s.totalProperty){
16705             var vt = parseInt(this.getTotal(o), 10);
16706             if(!isNaN(vt)){
16707                 totalRecords = vt;
16708             }
16709         }
16710         if(s.successProperty){
16711             var vs = this.getSuccess(o);
16712             if(vs === false || vs === 'false'){
16713                 success = false;
16714             }
16715         }
16716         var records = [];
16717         for(var i = 0; i < c; i++){
16718             var n = root[i];
16719             var values = {};
16720             var id = this.getId(n);
16721             for(var j = 0; j < fl; j++){
16722                 f = fi[j];
16723                                 var v = this.ef[j](n);
16724                                 if (!f.convert) {
16725                                         Roo.log('missing convert for ' + f.name);
16726                                         Roo.log(f);
16727                                         continue;
16728                                 }
16729                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16730             }
16731                         if (!Record) {
16732                                 return {
16733                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16734                                         success : false,
16735                                         records : [],
16736                                         totalRecords : 0
16737                                 };
16738                         }
16739             var record = new Record(values, id);
16740             record.json = n;
16741             records[i] = record;
16742         }
16743         return {
16744             raw : o,
16745             success : success,
16746             records : records,
16747             totalRecords : totalRecords
16748         };
16749     },
16750     // used when loading children.. @see loadDataFromChildren
16751     toLoadData: function(rec)
16752     {
16753         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16754         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16755         return { data : data, total : data.length };
16756         
16757     }
16758 });/*
16759  * Based on:
16760  * Ext JS Library 1.1.1
16761  * Copyright(c) 2006-2007, Ext JS, LLC.
16762  *
16763  * Originally Released Under LGPL - original licence link has changed is not relivant.
16764  *
16765  * Fork - LGPL
16766  * <script type="text/javascript">
16767  */
16768
16769 /**
16770  * @class Roo.data.ArrayReader
16771  * @extends Roo.data.DataReader
16772  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16773  * Each element of that Array represents a row of data fields. The
16774  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16775  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16776  * <p>
16777  * Example code:.
16778  * <pre><code>
16779 var RecordDef = Roo.data.Record.create([
16780     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16781     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16782 ]);
16783 var myReader = new Roo.data.ArrayReader({
16784     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16785 }, RecordDef);
16786 </code></pre>
16787  * <p>
16788  * This would consume an Array like this:
16789  * <pre><code>
16790 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16791   </code></pre>
16792  
16793  * @constructor
16794  * Create a new JsonReader
16795  * @param {Object} meta Metadata configuration options.
16796  * @param {Object|Array} recordType Either an Array of field definition objects
16797  * 
16798  * @cfg {Array} fields Array of field definition objects
16799  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16800  * as specified to {@link Roo.data.Record#create},
16801  * or an {@link Roo.data.Record} object
16802  *
16803  * 
16804  * created using {@link Roo.data.Record#create}.
16805  */
16806 Roo.data.ArrayReader = function(meta, recordType)
16807 {    
16808     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16809 };
16810
16811 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16812     
16813       /**
16814      * Create a data block containing Roo.data.Records from an XML document.
16815      * @param {Object} o An Array of row objects which represents the dataset.
16816      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16817      * a cache of Roo.data.Records.
16818      */
16819     readRecords : function(o)
16820     {
16821         var sid = this.meta ? this.meta.id : null;
16822         var recordType = this.recordType, fields = recordType.prototype.fields;
16823         var records = [];
16824         var root = o;
16825         for(var i = 0; i < root.length; i++){
16826             var n = root[i];
16827             var values = {};
16828             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16829             for(var j = 0, jlen = fields.length; j < jlen; j++){
16830                 var f = fields.items[j];
16831                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16832                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16833                 v = f.convert(v);
16834                 values[f.name] = v;
16835             }
16836             var record = new recordType(values, id);
16837             record.json = n;
16838             records[records.length] = record;
16839         }
16840         return {
16841             records : records,
16842             totalRecords : records.length
16843         };
16844     },
16845     // used when loading children.. @see loadDataFromChildren
16846     toLoadData: function(rec)
16847     {
16848         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16849         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16850         
16851     }
16852     
16853     
16854 });/*
16855  * - LGPL
16856  * * 
16857  */
16858
16859 /**
16860  * @class Roo.bootstrap.form.ComboBox
16861  * @extends Roo.bootstrap.form.TriggerField
16862  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16863  * @cfg {Boolean} append (true|false) default false
16864  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16865  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16866  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16867  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16868  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16869  * @cfg {Boolean} animate default true
16870  * @cfg {Boolean} emptyResultText only for touch device
16871  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16872  * @cfg {String} emptyTitle default ''
16873  * @cfg {Number} width fixed with? experimental
16874  * @constructor
16875  * Create a new ComboBox.
16876  * @param {Object} config Configuration options
16877  */
16878 Roo.bootstrap.form.ComboBox = function(config){
16879     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16880     this.addEvents({
16881         /**
16882          * @event expand
16883          * Fires when the dropdown list is expanded
16884         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16885         */
16886         'expand' : true,
16887         /**
16888          * @event collapse
16889          * Fires when the dropdown list is collapsed
16890         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16891         */
16892         'collapse' : true,
16893         /**
16894          * @event beforeselect
16895          * Fires before a list item is selected. Return false to cancel the selection.
16896         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897         * @param {Roo.data.Record} record The data record returned from the underlying store
16898         * @param {Number} index The index of the selected item in the dropdown list
16899         */
16900         'beforeselect' : true,
16901         /**
16902          * @event select
16903          * Fires when a list item is selected
16904         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16905         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16906         * @param {Number} index The index of the selected item in the dropdown list
16907         */
16908         'select' : true,
16909         /**
16910          * @event beforequery
16911          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16912          * The event object passed has these properties:
16913         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914         * @param {String} query The query
16915         * @param {Boolean} forceAll true to force "all" query
16916         * @param {Boolean} cancel true to cancel the query
16917         * @param {Object} e The query event object
16918         */
16919         'beforequery': true,
16920          /**
16921          * @event add
16922          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16923         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924         */
16925         'add' : true,
16926         /**
16927          * @event edit
16928          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16929         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16931         */
16932         'edit' : true,
16933         /**
16934          * @event remove
16935          * Fires when the remove value from the combobox array
16936         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16937         */
16938         'remove' : true,
16939         /**
16940          * @event afterremove
16941          * Fires when the remove value from the combobox array
16942         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16943         */
16944         'afterremove' : true,
16945         /**
16946          * @event specialfilter
16947          * Fires when specialfilter
16948             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16949             */
16950         'specialfilter' : true,
16951         /**
16952          * @event tick
16953          * Fires when tick the element
16954             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16955             */
16956         'tick' : true,
16957         /**
16958          * @event touchviewdisplay
16959          * Fires when touch view require special display (default is using displayField)
16960             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16961             * @param {Object} cfg set html .
16962             */
16963         'touchviewdisplay' : true
16964         
16965     });
16966     
16967     this.item = [];
16968     this.tickItems = [];
16969     
16970     this.selectedIndex = -1;
16971     if(this.mode == 'local'){
16972         if(config.queryDelay === undefined){
16973             this.queryDelay = 10;
16974         }
16975         if(config.minChars === undefined){
16976             this.minChars = 0;
16977         }
16978     }
16979 };
16980
16981 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16982      
16983     /**
16984      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16985      * rendering into an Roo.Editor, defaults to false)
16986      */
16987     /**
16988      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16989      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16990      */
16991     /**
16992      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16993      */
16994     /**
16995      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16996      * the dropdown list (defaults to undefined, with no header element)
16997      */
16998
16999      /**
17000      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17001      */
17002      
17003      /**
17004      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17005      */
17006     listWidth: undefined,
17007     /**
17008      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17009      * mode = 'remote' or 'text' if mode = 'local')
17010      */
17011     displayField: undefined,
17012     
17013     /**
17014      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17015      * mode = 'remote' or 'value' if mode = 'local'). 
17016      * Note: use of a valueField requires the user make a selection
17017      * in order for a value to be mapped.
17018      */
17019     valueField: undefined,
17020     /**
17021      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17022      */
17023     modalTitle : '',
17024     
17025     /**
17026      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17027      * field's data value (defaults to the underlying DOM element's name)
17028      */
17029     hiddenName: undefined,
17030     /**
17031      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17032      */
17033     listClass: '',
17034     /**
17035      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17036      */
17037     selectedClass: 'active',
17038     
17039     /**
17040      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17041      */
17042     shadow:'sides',
17043     /**
17044      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17045      * anchor positions (defaults to 'tl-bl')
17046      */
17047     listAlign: 'tl-bl?',
17048     /**
17049      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17050      */
17051     maxHeight: 300,
17052     /**
17053      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17054      * query specified by the allQuery config option (defaults to 'query')
17055      */
17056     triggerAction: 'query',
17057     /**
17058      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17059      * (defaults to 4, does not apply if editable = false)
17060      */
17061     minChars : 4,
17062     /**
17063      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17064      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17065      */
17066     typeAhead: false,
17067     /**
17068      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17069      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17070      */
17071     queryDelay: 500,
17072     /**
17073      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17074      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17075      */
17076     pageSize: 0,
17077     /**
17078      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17079      * when editable = true (defaults to false)
17080      */
17081     selectOnFocus:false,
17082     /**
17083      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17084      */
17085     queryParam: 'query',
17086     /**
17087      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17088      * when mode = 'remote' (defaults to 'Loading...')
17089      */
17090     loadingText: 'Loading...',
17091     /**
17092      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17093      */
17094     resizable: false,
17095     /**
17096      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17097      */
17098     handleHeight : 8,
17099     /**
17100      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17101      * traditional select (defaults to true)
17102      */
17103     editable: true,
17104     /**
17105      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17106      */
17107     allQuery: '',
17108     /**
17109      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17110      */
17111     mode: 'remote',
17112     /**
17113      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17114      * listWidth has a higher value)
17115      */
17116     minListWidth : 70,
17117     /**
17118      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17119      * allow the user to set arbitrary text into the field (defaults to false)
17120      */
17121     forceSelection:false,
17122     /**
17123      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17124      * if typeAhead = true (defaults to 250)
17125      */
17126     typeAheadDelay : 250,
17127     /**
17128      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17129      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17130      */
17131     valueNotFoundText : undefined,
17132     /**
17133      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17134      */
17135     blockFocus : false,
17136     
17137     /**
17138      * @cfg {Boolean} disableClear Disable showing of clear button.
17139      */
17140     disableClear : false,
17141     /**
17142      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17143      */
17144     alwaysQuery : false,
17145     
17146     /**
17147      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17148      */
17149     multiple : false,
17150     
17151     /**
17152      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17153      */
17154     invalidClass : "has-warning",
17155     
17156     /**
17157      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17158      */
17159     validClass : "has-success",
17160     
17161     /**
17162      * @cfg {Boolean} specialFilter (true|false) special filter default false
17163      */
17164     specialFilter : false,
17165     
17166     /**
17167      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17168      */
17169     mobileTouchView : true,
17170     
17171     /**
17172      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17173      */
17174     useNativeIOS : false,
17175     
17176     /**
17177      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17178      */
17179     mobile_restrict_height : false,
17180     
17181     ios_options : false,
17182     
17183     //private
17184     addicon : false,
17185     editicon: false,
17186     
17187     page: 0,
17188     hasQuery: false,
17189     append: false,
17190     loadNext: false,
17191     autoFocus : true,
17192     tickable : false,
17193     btnPosition : 'right',
17194     triggerList : true,
17195     showToggleBtn : true,
17196     animate : true,
17197     emptyResultText: 'Empty',
17198     triggerText : 'Select',
17199     emptyTitle : '',
17200     width : false,
17201     
17202     // element that contains real text value.. (when hidden is used..)
17203     
17204     getAutoCreate : function()
17205     {   
17206         var cfg = false;
17207         //render
17208         /*
17209          * Render classic select for iso
17210          */
17211         
17212         if(Roo.isIOS && this.useNativeIOS){
17213             cfg = this.getAutoCreateNativeIOS();
17214             return cfg;
17215         }
17216         
17217         /*
17218          * Touch Devices
17219          */
17220         
17221         if(Roo.isTouch && this.mobileTouchView){
17222             cfg = this.getAutoCreateTouchView();
17223             return cfg;;
17224         }
17225         
17226         /*
17227          *  Normal ComboBox
17228          */
17229         if(!this.tickable){
17230             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17231             return cfg;
17232         }
17233         
17234         /*
17235          *  ComboBox with tickable selections
17236          */
17237              
17238         var align = this.labelAlign || this.parentLabelAlign();
17239         
17240         cfg = {
17241             cls : 'form-group roo-combobox-tickable' //input-group
17242         };
17243         
17244         var btn_text_select = '';
17245         var btn_text_done = '';
17246         var btn_text_cancel = '';
17247         
17248         if (this.btn_text_show) {
17249             btn_text_select = 'Select';
17250             btn_text_done = 'Done';
17251             btn_text_cancel = 'Cancel'; 
17252         }
17253         
17254         var buttons = {
17255             tag : 'div',
17256             cls : 'tickable-buttons',
17257             cn : [
17258                 {
17259                     tag : 'button',
17260                     type : 'button',
17261                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17262                     //html : this.triggerText
17263                     html: btn_text_select
17264                 },
17265                 {
17266                     tag : 'button',
17267                     type : 'button',
17268                     name : 'ok',
17269                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17270                     //html : 'Done'
17271                     html: btn_text_done
17272                 },
17273                 {
17274                     tag : 'button',
17275                     type : 'button',
17276                     name : 'cancel',
17277                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17278                     //html : 'Cancel'
17279                     html: btn_text_cancel
17280                 }
17281             ]
17282         };
17283         
17284         if(this.editable){
17285             buttons.cn.unshift({
17286                 tag: 'input',
17287                 cls: 'roo-select2-search-field-input'
17288             });
17289         }
17290         
17291         var _this = this;
17292         
17293         Roo.each(buttons.cn, function(c){
17294             if (_this.size) {
17295                 c.cls += ' btn-' + _this.size;
17296             }
17297
17298             if (_this.disabled) {
17299                 c.disabled = true;
17300             }
17301         });
17302         
17303         var box = {
17304             tag: 'div',
17305             style : 'display: contents',
17306             cn: [
17307                 {
17308                     tag: 'input',
17309                     type : 'hidden',
17310                     cls: 'form-hidden-field'
17311                 },
17312                 {
17313                     tag: 'ul',
17314                     cls: 'roo-select2-choices',
17315                     cn:[
17316                         {
17317                             tag: 'li',
17318                             cls: 'roo-select2-search-field',
17319                             cn: [
17320                                 buttons
17321                             ]
17322                         }
17323                     ]
17324                 }
17325             ]
17326         };
17327         
17328         var combobox = {
17329             cls: 'roo-select2-container input-group roo-select2-container-multi',
17330             cn: [
17331                 
17332                 box
17333 //                {
17334 //                    tag: 'ul',
17335 //                    cls: 'typeahead typeahead-long dropdown-menu',
17336 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17337 //                }
17338             ]
17339         };
17340         
17341         if(this.hasFeedback && !this.allowBlank){
17342             
17343             var feedback = {
17344                 tag: 'span',
17345                 cls: 'glyphicon form-control-feedback'
17346             };
17347
17348             combobox.cn.push(feedback);
17349         }
17350         
17351         
17352         
17353         var indicator = {
17354             tag : 'i',
17355             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17356             tooltip : 'This field is required'
17357         };
17358         if (Roo.bootstrap.version == 4) {
17359             indicator = {
17360                 tag : 'i',
17361                 style : 'display:none'
17362             };
17363         }
17364         if (align ==='left' && this.fieldLabel.length) {
17365             
17366             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17367             
17368             cfg.cn = [
17369                 indicator,
17370                 {
17371                     tag: 'label',
17372                     'for' :  id,
17373                     cls : 'control-label col-form-label',
17374                     html : this.fieldLabel
17375
17376                 },
17377                 {
17378                     cls : "", 
17379                     cn: [
17380                         combobox
17381                     ]
17382                 }
17383
17384             ];
17385             
17386             var labelCfg = cfg.cn[1];
17387             var contentCfg = cfg.cn[2];
17388             
17389
17390             if(this.indicatorpos == 'right'){
17391                 
17392                 cfg.cn = [
17393                     {
17394                         tag: 'label',
17395                         'for' :  id,
17396                         cls : 'control-label col-form-label',
17397                         cn : [
17398                             {
17399                                 tag : 'span',
17400                                 html : this.fieldLabel
17401                             },
17402                             indicator
17403                         ]
17404                     },
17405                     {
17406                         cls : "",
17407                         cn: [
17408                             combobox
17409                         ]
17410                     }
17411
17412                 ];
17413                 
17414                 
17415                 
17416                 labelCfg = cfg.cn[0];
17417                 contentCfg = cfg.cn[1];
17418             
17419             }
17420             
17421             if(this.labelWidth > 12){
17422                 labelCfg.style = "width: " + this.labelWidth + 'px';
17423             }
17424             if(this.width * 1 > 0){
17425                 contentCfg.style = "width: " + this.width + 'px';
17426             }
17427             if(this.labelWidth < 13 && this.labelmd == 0){
17428                 this.labelmd = this.labelWidth;
17429             }
17430             
17431             if(this.labellg > 0){
17432                 labelCfg.cls += ' col-lg-' + this.labellg;
17433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17434             }
17435             
17436             if(this.labelmd > 0){
17437                 labelCfg.cls += ' col-md-' + this.labelmd;
17438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17439             }
17440             
17441             if(this.labelsm > 0){
17442                 labelCfg.cls += ' col-sm-' + this.labelsm;
17443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17444             }
17445             
17446             if(this.labelxs > 0){
17447                 labelCfg.cls += ' col-xs-' + this.labelxs;
17448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17449             }
17450                 
17451                 
17452         } else if ( this.fieldLabel.length) {
17453 //                Roo.log(" label");
17454                  cfg.cn = [
17455                    indicator,
17456                     {
17457                         tag: 'label',
17458                         //cls : 'input-group-addon',
17459                         html : this.fieldLabel
17460                     },
17461                     combobox
17462                 ];
17463                 
17464                 if(this.indicatorpos == 'right'){
17465                     cfg.cn = [
17466                         {
17467                             tag: 'label',
17468                             //cls : 'input-group-addon',
17469                             html : this.fieldLabel
17470                         },
17471                         indicator,
17472                         combobox
17473                     ];
17474                     
17475                 }
17476
17477         } else {
17478             
17479 //                Roo.log(" no label && no align");
17480                 cfg = combobox
17481                      
17482                 
17483         }
17484          
17485         var settings=this;
17486         ['xs','sm','md','lg'].map(function(size){
17487             if (settings[size]) {
17488                 cfg.cls += ' col-' + size + '-' + settings[size];
17489             }
17490         });
17491         
17492         return cfg;
17493         
17494     },
17495     
17496     _initEventsCalled : false,
17497     
17498     // private
17499     initEvents: function()
17500     {   
17501         if (this._initEventsCalled) { // as we call render... prevent looping...
17502             return;
17503         }
17504         this._initEventsCalled = true;
17505         
17506         if (!this.store) {
17507             throw "can not find store for combo";
17508         }
17509         
17510         this.indicator = this.indicatorEl();
17511         
17512         this.store = Roo.factory(this.store, Roo.data);
17513         this.store.parent = this;
17514         
17515         // if we are building from html. then this element is so complex, that we can not really
17516         // use the rendered HTML.
17517         // so we have to trash and replace the previous code.
17518         if (Roo.XComponent.build_from_html) {
17519             // remove this element....
17520             var e = this.el.dom, k=0;
17521             while (e ) { e = e.previousSibling;  ++k;}
17522
17523             this.el.remove();
17524             
17525             this.el=false;
17526             this.rendered = false;
17527             
17528             this.render(this.parent().getChildContainer(true), k);
17529         }
17530         
17531         if(Roo.isIOS && this.useNativeIOS){
17532             this.initIOSView();
17533             return;
17534         }
17535         
17536         /*
17537          * Touch Devices
17538          */
17539         
17540         if(Roo.isTouch && this.mobileTouchView){
17541             this.initTouchView();
17542             return;
17543         }
17544         
17545         if(this.tickable){
17546             this.initTickableEvents();
17547             return;
17548         }
17549         
17550         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17551         
17552         if(this.hiddenName){
17553             
17554             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17555             
17556             this.hiddenField.dom.value =
17557                 this.hiddenValue !== undefined ? this.hiddenValue :
17558                 this.value !== undefined ? this.value : '';
17559
17560             // prevent input submission
17561             this.el.dom.removeAttribute('name');
17562             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17563              
17564              
17565         }
17566         //if(Roo.isGecko){
17567         //    this.el.dom.setAttribute('autocomplete', 'off');
17568         //}
17569         
17570         var cls = 'x-combo-list';
17571         
17572         //this.list = new Roo.Layer({
17573         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17574         //});
17575         
17576         var _this = this;
17577         
17578         (function(){
17579             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17580             _this.list.setWidth(lw);
17581         }).defer(100);
17582         
17583         this.list.on('mouseover', this.onViewOver, this);
17584         this.list.on('mousemove', this.onViewMove, this);
17585         this.list.on('scroll', this.onViewScroll, this);
17586         
17587         /*
17588         this.list.swallowEvent('mousewheel');
17589         this.assetHeight = 0;
17590
17591         if(this.title){
17592             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17593             this.assetHeight += this.header.getHeight();
17594         }
17595
17596         this.innerList = this.list.createChild({cls:cls+'-inner'});
17597         this.innerList.on('mouseover', this.onViewOver, this);
17598         this.innerList.on('mousemove', this.onViewMove, this);
17599         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17600         
17601         if(this.allowBlank && !this.pageSize && !this.disableClear){
17602             this.footer = this.list.createChild({cls:cls+'-ft'});
17603             this.pageTb = new Roo.Toolbar(this.footer);
17604            
17605         }
17606         if(this.pageSize){
17607             this.footer = this.list.createChild({cls:cls+'-ft'});
17608             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17609                     {pageSize: this.pageSize});
17610             
17611         }
17612         
17613         if (this.pageTb && this.allowBlank && !this.disableClear) {
17614             var _this = this;
17615             this.pageTb.add(new Roo.Toolbar.Fill(), {
17616                 cls: 'x-btn-icon x-btn-clear',
17617                 text: '&#160;',
17618                 handler: function()
17619                 {
17620                     _this.collapse();
17621                     _this.clearValue();
17622                     _this.onSelect(false, -1);
17623                 }
17624             });
17625         }
17626         if (this.footer) {
17627             this.assetHeight += this.footer.getHeight();
17628         }
17629         */
17630             
17631         if(!this.tpl){
17632             this.tpl = Roo.bootstrap.version == 4 ?
17633                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17634                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17635         }
17636
17637         this.view = new Roo.View(this.list, this.tpl, {
17638             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17639         });
17640         //this.view.wrapEl.setDisplayed(false);
17641         this.view.on('click', this.onViewClick, this);
17642         
17643         
17644         this.store.on('beforeload', this.onBeforeLoad, this);
17645         this.store.on('load', this.onLoad, this);
17646         this.store.on('loadexception', this.onLoadException, this);
17647         /*
17648         if(this.resizable){
17649             this.resizer = new Roo.Resizable(this.list,  {
17650                pinned:true, handles:'se'
17651             });
17652             this.resizer.on('resize', function(r, w, h){
17653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17654                 this.listWidth = w;
17655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17656                 this.restrictHeight();
17657             }, this);
17658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17659         }
17660         */
17661         if(!this.editable){
17662             this.editable = true;
17663             this.setEditable(false);
17664         }
17665         
17666         /*
17667         
17668         if (typeof(this.events.add.listeners) != 'undefined') {
17669             
17670             this.addicon = this.wrap.createChild(
17671                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17672        
17673             this.addicon.on('click', function(e) {
17674                 this.fireEvent('add', this);
17675             }, this);
17676         }
17677         if (typeof(this.events.edit.listeners) != 'undefined') {
17678             
17679             this.editicon = this.wrap.createChild(
17680                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17681             if (this.addicon) {
17682                 this.editicon.setStyle('margin-left', '40px');
17683             }
17684             this.editicon.on('click', function(e) {
17685                 
17686                 // we fire even  if inothing is selected..
17687                 this.fireEvent('edit', this, this.lastData );
17688                 
17689             }, this);
17690         }
17691         */
17692         
17693         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17694             "up" : function(e){
17695                 this.inKeyMode = true;
17696                 this.selectPrev();
17697             },
17698
17699             "down" : function(e){
17700                 if(!this.isExpanded()){
17701                     this.onTriggerClick();
17702                 }else{
17703                     this.inKeyMode = true;
17704                     this.selectNext();
17705                 }
17706             },
17707
17708             "enter" : function(e){
17709 //                this.onViewClick();
17710                 //return true;
17711                 this.collapse();
17712                 
17713                 if(this.fireEvent("specialkey", this, e)){
17714                     this.onViewClick(false);
17715                 }
17716                 
17717                 return true;
17718             },
17719
17720             "esc" : function(e){
17721                 this.collapse();
17722             },
17723
17724             "tab" : function(e){
17725                 this.collapse();
17726                 
17727                 if(this.fireEvent("specialkey", this, e)){
17728                     this.onViewClick(false);
17729                 }
17730                 
17731                 return true;
17732             },
17733
17734             scope : this,
17735
17736             doRelay : function(foo, bar, hname){
17737                 if(hname == 'down' || this.scope.isExpanded()){
17738                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17739                 }
17740                 return true;
17741             },
17742
17743             forceKeyDown: true
17744         });
17745         
17746         
17747         this.queryDelay = Math.max(this.queryDelay || 10,
17748                 this.mode == 'local' ? 10 : 250);
17749         
17750         
17751         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17752         
17753         if(this.typeAhead){
17754             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17755         }
17756         if(this.editable !== false){
17757             this.inputEl().on("keyup", this.onKeyUp, this);
17758         }
17759         if(this.forceSelection){
17760             this.inputEl().on('blur', this.doForce, this);
17761         }
17762         
17763         if(this.multiple){
17764             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17765             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17766         }
17767     },
17768     
17769     initTickableEvents: function()
17770     {   
17771         this.createList();
17772         
17773         if(this.hiddenName){
17774             
17775             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17776             
17777             this.hiddenField.dom.value =
17778                 this.hiddenValue !== undefined ? this.hiddenValue :
17779                 this.value !== undefined ? this.value : '';
17780
17781             // prevent input submission
17782             this.el.dom.removeAttribute('name');
17783             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17784              
17785              
17786         }
17787         
17788 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17789         
17790         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17791         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17792         if(this.triggerList){
17793             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17794         }
17795          
17796         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17797         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17798         
17799         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17800         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17801         
17802         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17803         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17804         
17805         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17806         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17807         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17808         
17809         this.okBtn.hide();
17810         this.cancelBtn.hide();
17811         
17812         var _this = this;
17813         
17814         (function(){
17815             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17816             _this.list.setWidth(lw);
17817         }).defer(100);
17818         
17819         this.list.on('mouseover', this.onViewOver, this);
17820         this.list.on('mousemove', this.onViewMove, this);
17821         
17822         this.list.on('scroll', this.onViewScroll, this);
17823         
17824         if(!this.tpl){
17825             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17826                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17827         }
17828
17829         this.view = new Roo.View(this.list, this.tpl, {
17830             singleSelect:true,
17831             tickable:true,
17832             parent:this,
17833             store: this.store,
17834             selectedClass: this.selectedClass
17835         });
17836         
17837         //this.view.wrapEl.setDisplayed(false);
17838         this.view.on('click', this.onViewClick, this);
17839         
17840         
17841         
17842         this.store.on('beforeload', this.onBeforeLoad, this);
17843         this.store.on('load', this.onLoad, this);
17844         this.store.on('loadexception', this.onLoadException, this);
17845         
17846         if(this.editable){
17847             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17848                 "up" : function(e){
17849                     this.inKeyMode = true;
17850                     this.selectPrev();
17851                 },
17852
17853                 "down" : function(e){
17854                     this.inKeyMode = true;
17855                     this.selectNext();
17856                 },
17857
17858                 "enter" : function(e){
17859                     if(this.fireEvent("specialkey", this, e)){
17860                         this.onViewClick(false);
17861                     }
17862                     
17863                     return true;
17864                 },
17865
17866                 "esc" : function(e){
17867                     this.onTickableFooterButtonClick(e, false, false);
17868                 },
17869
17870                 "tab" : function(e){
17871                     this.fireEvent("specialkey", this, e);
17872                     
17873                     this.onTickableFooterButtonClick(e, false, false);
17874                     
17875                     return true;
17876                 },
17877
17878                 scope : this,
17879
17880                 doRelay : function(e, fn, key){
17881                     if(this.scope.isExpanded()){
17882                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17883                     }
17884                     return true;
17885                 },
17886
17887                 forceKeyDown: true
17888             });
17889         }
17890         
17891         this.queryDelay = Math.max(this.queryDelay || 10,
17892                 this.mode == 'local' ? 10 : 250);
17893         
17894         
17895         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17896         
17897         if(this.typeAhead){
17898             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17899         }
17900         
17901         if(this.editable !== false){
17902             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17903         }
17904         
17905         this.indicator = this.indicatorEl();
17906         
17907         if(this.indicator){
17908             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17909             this.indicator.hide();
17910         }
17911         
17912     },
17913
17914     onDestroy : function(){
17915         if(this.view){
17916             this.view.setStore(null);
17917             this.view.el.removeAllListeners();
17918             this.view.el.remove();
17919             this.view.purgeListeners();
17920         }
17921         if(this.list){
17922             this.list.dom.innerHTML  = '';
17923         }
17924         
17925         if(this.store){
17926             this.store.un('beforeload', this.onBeforeLoad, this);
17927             this.store.un('load', this.onLoad, this);
17928             this.store.un('loadexception', this.onLoadException, this);
17929         }
17930         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17931     },
17932
17933     // private
17934     fireKey : function(e){
17935         if(e.isNavKeyPress() && !this.list.isVisible()){
17936             this.fireEvent("specialkey", this, e);
17937         }
17938     },
17939
17940     // private
17941     onResize: function(w, h)
17942     {
17943         
17944         
17945 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17946 //        
17947 //        if(typeof w != 'number'){
17948 //            // we do not handle it!?!?
17949 //            return;
17950 //        }
17951 //        var tw = this.trigger.getWidth();
17952 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17953 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17954 //        var x = w - tw;
17955 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17956 //            
17957 //        //this.trigger.setStyle('left', x+'px');
17958 //        
17959 //        if(this.list && this.listWidth === undefined){
17960 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17961 //            this.list.setWidth(lw);
17962 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17963 //        }
17964         
17965     
17966         
17967     },
17968
17969     /**
17970      * Allow or prevent the user from directly editing the field text.  If false is passed,
17971      * the user will only be able to select from the items defined in the dropdown list.  This method
17972      * is the runtime equivalent of setting the 'editable' config option at config time.
17973      * @param {Boolean} value True to allow the user to directly edit the field text
17974      */
17975     setEditable : function(value){
17976         if(value == this.editable){
17977             return;
17978         }
17979         this.editable = value;
17980         if(!value){
17981             this.inputEl().dom.setAttribute('readOnly', true);
17982             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17983             this.inputEl().addClass('x-combo-noedit');
17984         }else{
17985             this.inputEl().dom.removeAttribute('readOnly');
17986             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17987             this.inputEl().removeClass('x-combo-noedit');
17988         }
17989     },
17990
17991     // private
17992     
17993     onBeforeLoad : function(combo,opts){
17994         if(!this.hasFocus){
17995             return;
17996         }
17997          if (!opts.add) {
17998             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17999          }
18000         this.restrictHeight();
18001         this.selectedIndex = -1;
18002     },
18003
18004     // private
18005     onLoad : function(){
18006         
18007         this.hasQuery = false;
18008         
18009         if(!this.hasFocus){
18010             return;
18011         }
18012         
18013         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18014             this.loading.hide();
18015         }
18016         
18017         if(this.store.getCount() > 0){
18018             
18019             this.expand();
18020             this.restrictHeight();
18021             if(this.lastQuery == this.allQuery){
18022                 if(this.editable && !this.tickable){
18023                     this.inputEl().dom.select();
18024                 }
18025                 
18026                 if(
18027                     !this.selectByValue(this.value, true) &&
18028                     this.autoFocus && 
18029                     (
18030                         !this.store.lastOptions ||
18031                         typeof(this.store.lastOptions.add) == 'undefined' || 
18032                         this.store.lastOptions.add != true
18033                     )
18034                 ){
18035                     this.select(0, true);
18036                 }
18037             }else{
18038                 if(this.autoFocus){
18039                     this.selectNext();
18040                 }
18041                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18042                     this.taTask.delay(this.typeAheadDelay);
18043                 }
18044             }
18045         }else{
18046             this.onEmptyResults();
18047         }
18048         
18049         //this.el.focus();
18050     },
18051     // private
18052     onLoadException : function()
18053     {
18054         this.hasQuery = false;
18055         
18056         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18057             this.loading.hide();
18058         }
18059         
18060         if(this.tickable && this.editable){
18061             return;
18062         }
18063         
18064         this.collapse();
18065         // only causes errors at present
18066         //Roo.log(this.store.reader.jsonData);
18067         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18068             // fixme
18069             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18070         //}
18071         
18072         
18073     },
18074     // private
18075     onTypeAhead : function(){
18076         if(this.store.getCount() > 0){
18077             var r = this.store.getAt(0);
18078             var newValue = r.data[this.displayField];
18079             var len = newValue.length;
18080             var selStart = this.getRawValue().length;
18081             
18082             if(selStart != len){
18083                 this.setRawValue(newValue);
18084                 this.selectText(selStart, newValue.length);
18085             }
18086         }
18087     },
18088
18089     // private
18090     onSelect : function(record, index){
18091         
18092         if(this.fireEvent('beforeselect', this, record, index) !== false){
18093         
18094             this.setFromData(index > -1 ? record.data : false);
18095             
18096             this.collapse();
18097             this.fireEvent('select', this, record, index);
18098         }
18099     },
18100
18101     /**
18102      * Returns the currently selected field value or empty string if no value is set.
18103      * @return {String} value The selected value
18104      */
18105     getValue : function()
18106     {
18107         if(Roo.isIOS && this.useNativeIOS){
18108             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18109         }
18110         
18111         if(this.multiple){
18112             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18113         }
18114         
18115         if(this.valueField){
18116             return typeof this.value != 'undefined' ? this.value : '';
18117         }else{
18118             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18119         }
18120     },
18121     
18122     getRawValue : function()
18123     {
18124         if(Roo.isIOS && this.useNativeIOS){
18125             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18126         }
18127         
18128         var v = this.inputEl().getValue();
18129         
18130         return v;
18131     },
18132
18133     /**
18134      * Clears any text/value currently set in the field
18135      */
18136     clearValue : function(){
18137         
18138         if(this.hiddenField){
18139             this.hiddenField.dom.value = '';
18140         }
18141         this.value = '';
18142         this.setRawValue('');
18143         this.lastSelectionText = '';
18144         this.lastData = false;
18145         
18146         var close = this.closeTriggerEl();
18147         
18148         if(close){
18149             close.hide();
18150         }
18151         
18152         this.validate();
18153         
18154     },
18155
18156     /**
18157      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18158      * will be displayed in the field.  If the value does not match the data value of an existing item,
18159      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18160      * Otherwise the field will be blank (although the value will still be set).
18161      * @param {String} value The value to match
18162      */
18163     setValue : function(v)
18164     {
18165         if(Roo.isIOS && this.useNativeIOS){
18166             this.setIOSValue(v);
18167             return;
18168         }
18169         
18170         if(this.multiple){
18171             this.syncValue();
18172             return;
18173         }
18174         
18175         var text = v;
18176         if(this.valueField){
18177             var r = this.findRecord(this.valueField, v);
18178             if(r){
18179                 text = r.data[this.displayField];
18180             }else if(this.valueNotFoundText !== undefined){
18181                 text = this.valueNotFoundText;
18182             }
18183         }
18184         this.lastSelectionText = text;
18185         if(this.hiddenField){
18186             this.hiddenField.dom.value = v;
18187         }
18188         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18189         this.value = v;
18190         
18191         var close = this.closeTriggerEl();
18192         
18193         if(close){
18194             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18195         }
18196         
18197         this.validate();
18198     },
18199     /**
18200      * @property {Object} the last set data for the element
18201      */
18202     
18203     lastData : false,
18204     /**
18205      * Sets the value of the field based on a object which is related to the record format for the store.
18206      * @param {Object} value the value to set as. or false on reset?
18207      */
18208     setFromData : function(o){
18209         
18210         if(this.multiple){
18211             this.addItem(o);
18212             return;
18213         }
18214             
18215         var dv = ''; // display value
18216         var vv = ''; // value value..
18217         this.lastData = o;
18218         if (this.displayField) {
18219             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18220         } else {
18221             // this is an error condition!!!
18222             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18223         }
18224         
18225         if(this.valueField){
18226             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18227         }
18228         
18229         var close = this.closeTriggerEl();
18230         
18231         if(close){
18232             if(dv.length || vv * 1 > 0){
18233                 close.show() ;
18234                 this.blockFocus=true;
18235             } else {
18236                 close.hide();
18237             }             
18238         }
18239         
18240         if(this.hiddenField){
18241             this.hiddenField.dom.value = vv;
18242             
18243             this.lastSelectionText = dv;
18244             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18245             this.value = vv;
18246             return;
18247         }
18248         // no hidden field.. - we store the value in 'value', but still display
18249         // display field!!!!
18250         this.lastSelectionText = dv;
18251         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18252         this.value = vv;
18253         
18254         
18255         
18256     },
18257     // private
18258     reset : function(){
18259         // overridden so that last data is reset..
18260         
18261         if(this.multiple){
18262             this.clearItem();
18263             return;
18264         }
18265         
18266         this.setValue(this.originalValue);
18267         //this.clearInvalid();
18268         this.lastData = false;
18269         if (this.view) {
18270             this.view.clearSelections();
18271         }
18272         
18273         this.validate();
18274     },
18275     // private
18276     findRecord : function(prop, value){
18277         var record;
18278         if(this.store.getCount() > 0){
18279             this.store.each(function(r){
18280                 if(r.data[prop] == value){
18281                     record = r;
18282                     return false;
18283                 }
18284                 return true;
18285             });
18286         }
18287         return record;
18288     },
18289     
18290     getName: function()
18291     {
18292         // returns hidden if it's set..
18293         if (!this.rendered) {return ''};
18294         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18295         
18296     },
18297     // private
18298     onViewMove : function(e, t){
18299         this.inKeyMode = false;
18300     },
18301
18302     // private
18303     onViewOver : function(e, t){
18304         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18305             return;
18306         }
18307         var item = this.view.findItemFromChild(t);
18308         
18309         if(item){
18310             var index = this.view.indexOf(item);
18311             this.select(index, false);
18312         }
18313     },
18314
18315     // private
18316     onViewClick : function(view, doFocus, el, e)
18317     {
18318         var index = this.view.getSelectedIndexes()[0];
18319         
18320         var r = this.store.getAt(index);
18321         
18322         if(this.tickable){
18323             
18324             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18325                 return;
18326             }
18327             
18328             var rm = false;
18329             var _this = this;
18330             
18331             Roo.each(this.tickItems, function(v,k){
18332                 
18333                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18334                     Roo.log(v);
18335                     _this.tickItems.splice(k, 1);
18336                     
18337                     if(typeof(e) == 'undefined' && view == false){
18338                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18339                     }
18340                     
18341                     rm = true;
18342                     return;
18343                 }
18344             });
18345             
18346             if(rm){
18347                 return;
18348             }
18349             
18350             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18351                 this.tickItems.push(r.data);
18352             }
18353             
18354             if(typeof(e) == 'undefined' && view == false){
18355                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18356             }
18357                     
18358             return;
18359         }
18360         
18361         if(r){
18362             this.onSelect(r, index);
18363         }
18364         if(doFocus !== false && !this.blockFocus){
18365             this.inputEl().focus();
18366         }
18367     },
18368
18369     // private
18370     restrictHeight : function(){
18371         //this.innerList.dom.style.height = '';
18372         //var inner = this.innerList.dom;
18373         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18374         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18375         //this.list.beginUpdate();
18376         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18377         this.list.alignTo(this.inputEl(), this.listAlign);
18378         this.list.alignTo(this.inputEl(), this.listAlign);
18379         //this.list.endUpdate();
18380     },
18381
18382     // private
18383     onEmptyResults : function(){
18384         
18385         if(this.tickable && this.editable){
18386             this.hasFocus = false;
18387             this.restrictHeight();
18388             return;
18389         }
18390         
18391         this.collapse();
18392     },
18393
18394     /**
18395      * Returns true if the dropdown list is expanded, else false.
18396      */
18397     isExpanded : function(){
18398         return this.list.isVisible();
18399     },
18400
18401     /**
18402      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18403      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18404      * @param {String} value The data value of the item to select
18405      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18406      * selected item if it is not currently in view (defaults to true)
18407      * @return {Boolean} True if the value matched an item in the list, else false
18408      */
18409     selectByValue : function(v, scrollIntoView){
18410         if(v !== undefined && v !== null){
18411             var r = this.findRecord(this.valueField || this.displayField, v);
18412             if(r){
18413                 this.select(this.store.indexOf(r), scrollIntoView);
18414                 return true;
18415             }
18416         }
18417         return false;
18418     },
18419
18420     /**
18421      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18422      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18423      * @param {Number} index The zero-based index of the list item to select
18424      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18425      * selected item if it is not currently in view (defaults to true)
18426      */
18427     select : function(index, scrollIntoView){
18428         this.selectedIndex = index;
18429         this.view.select(index);
18430         if(scrollIntoView !== false){
18431             var el = this.view.getNode(index);
18432             /*
18433              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18434              */
18435             if(el){
18436                 this.list.scrollChildIntoView(el, false);
18437             }
18438         }
18439     },
18440
18441     // private
18442     selectNext : function(){
18443         var ct = this.store.getCount();
18444         if(ct > 0){
18445             if(this.selectedIndex == -1){
18446                 this.select(0);
18447             }else if(this.selectedIndex < ct-1){
18448                 this.select(this.selectedIndex+1);
18449             }
18450         }
18451     },
18452
18453     // private
18454     selectPrev : function(){
18455         var ct = this.store.getCount();
18456         if(ct > 0){
18457             if(this.selectedIndex == -1){
18458                 this.select(0);
18459             }else if(this.selectedIndex != 0){
18460                 this.select(this.selectedIndex-1);
18461             }
18462         }
18463     },
18464
18465     // private
18466     onKeyUp : function(e){
18467         if(this.editable !== false && !e.isSpecialKey()){
18468             this.lastKey = e.getKey();
18469             this.dqTask.delay(this.queryDelay);
18470         }
18471     },
18472
18473     // private
18474     validateBlur : function(){
18475         return !this.list || !this.list.isVisible();   
18476     },
18477
18478     // private
18479     initQuery : function(){
18480         
18481         var v = this.getRawValue();
18482         
18483         if(this.tickable && this.editable){
18484             v = this.tickableInputEl().getValue();
18485         }
18486         
18487         this.doQuery(v);
18488     },
18489
18490     // private
18491     doForce : function(){
18492         if(this.inputEl().dom.value.length > 0){
18493             this.inputEl().dom.value =
18494                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18495              
18496         }
18497     },
18498
18499     /**
18500      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18501      * query allowing the query action to be canceled if needed.
18502      * @param {String} query The SQL query to execute
18503      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18504      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18505      * saved in the current store (defaults to false)
18506      */
18507     doQuery : function(q, forceAll){
18508         
18509         if(q === undefined || q === null){
18510             q = '';
18511         }
18512         var qe = {
18513             query: q,
18514             forceAll: forceAll,
18515             combo: this,
18516             cancel:false
18517         };
18518         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18519             return false;
18520         }
18521         q = qe.query;
18522         
18523         forceAll = qe.forceAll;
18524         if(forceAll === true || (q.length >= this.minChars)){
18525             
18526             this.hasQuery = true;
18527             
18528             if(this.lastQuery != q || this.alwaysQuery){
18529                 this.lastQuery = q;
18530                 if(this.mode == 'local'){
18531                     this.selectedIndex = -1;
18532                     if(forceAll){
18533                         this.store.clearFilter();
18534                     }else{
18535                         
18536                         if(this.specialFilter){
18537                             this.fireEvent('specialfilter', this);
18538                             this.onLoad();
18539                             return;
18540                         }
18541                         
18542                         this.store.filter(this.displayField, q);
18543                     }
18544                     
18545                     this.store.fireEvent("datachanged", this.store);
18546                     
18547                     this.onLoad();
18548                     
18549                     
18550                 }else{
18551                     
18552                     this.store.baseParams[this.queryParam] = q;
18553                     
18554                     var options = {params : this.getParams(q)};
18555                     
18556                     if(this.loadNext){
18557                         options.add = true;
18558                         options.params.start = this.page * this.pageSize;
18559                     }
18560                     
18561                     this.store.load(options);
18562                     
18563                     /*
18564                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18565                      *  we should expand the list on onLoad
18566                      *  so command out it
18567                      */
18568 //                    this.expand();
18569                 }
18570             }else{
18571                 this.selectedIndex = -1;
18572                 this.onLoad();   
18573             }
18574         }
18575         
18576         this.loadNext = false;
18577     },
18578     
18579     // private
18580     getParams : function(q){
18581         var p = {};
18582         //p[this.queryParam] = q;
18583         
18584         if(this.pageSize){
18585             p.start = 0;
18586             p.limit = this.pageSize;
18587         }
18588         return p;
18589     },
18590
18591     /**
18592      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18593      */
18594     collapse : function(){
18595         if(!this.isExpanded()){
18596             return;
18597         }
18598         
18599         this.list.hide();
18600         
18601         this.hasFocus = false;
18602         
18603         if(this.tickable){
18604             this.okBtn.hide();
18605             this.cancelBtn.hide();
18606             this.trigger.show();
18607             
18608             if(this.editable){
18609                 this.tickableInputEl().dom.value = '';
18610                 this.tickableInputEl().blur();
18611             }
18612             
18613         }
18614         
18615         Roo.get(document).un('mousedown', this.collapseIf, this);
18616         Roo.get(document).un('mousewheel', this.collapseIf, this);
18617         if (!this.editable) {
18618             Roo.get(document).un('keydown', this.listKeyPress, this);
18619         }
18620         this.fireEvent('collapse', this);
18621         
18622         this.validate();
18623     },
18624
18625     // private
18626     collapseIf : function(e){
18627         var in_combo  = e.within(this.el);
18628         var in_list =  e.within(this.list);
18629         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18630         
18631         if (in_combo || in_list || is_list) {
18632             //e.stopPropagation();
18633             return;
18634         }
18635         
18636         if(this.tickable){
18637             this.onTickableFooterButtonClick(e, false, false);
18638         }
18639
18640         this.collapse();
18641         
18642     },
18643
18644     /**
18645      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18646      */
18647     expand : function(){
18648        
18649         if(this.isExpanded() || !this.hasFocus){
18650             return;
18651         }
18652         
18653         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18654         this.list.setWidth(lw);
18655         
18656         Roo.log('expand');
18657         
18658         this.list.show();
18659         
18660         this.restrictHeight();
18661         
18662         if(this.tickable){
18663             
18664             this.tickItems = Roo.apply([], this.item);
18665             
18666             this.okBtn.show();
18667             this.cancelBtn.show();
18668             this.trigger.hide();
18669             
18670             if(this.editable){
18671                 this.tickableInputEl().focus();
18672             }
18673             
18674         }
18675         
18676         Roo.get(document).on('mousedown', this.collapseIf, this);
18677         Roo.get(document).on('mousewheel', this.collapseIf, this);
18678         if (!this.editable) {
18679             Roo.get(document).on('keydown', this.listKeyPress, this);
18680         }
18681         
18682         this.fireEvent('expand', this);
18683     },
18684
18685     // private
18686     // Implements the default empty TriggerField.onTriggerClick function
18687     onTriggerClick : function(e)
18688     {
18689         Roo.log('trigger click');
18690         
18691         if(this.disabled || !this.triggerList){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         
18698         if(this.isExpanded()){
18699             this.collapse();
18700             if (!this.blockFocus) {
18701                 this.inputEl().focus();
18702             }
18703             
18704         }else {
18705             this.hasFocus = true;
18706             if(this.triggerAction == 'all') {
18707                 this.doQuery(this.allQuery, true);
18708             } else {
18709                 this.doQuery(this.getRawValue());
18710             }
18711             if (!this.blockFocus) {
18712                 this.inputEl().focus();
18713             }
18714         }
18715     },
18716     
18717     onTickableTriggerClick : function(e)
18718     {
18719         if(this.disabled){
18720             return;
18721         }
18722         
18723         this.page = 0;
18724         this.loadNext = false;
18725         this.hasFocus = true;
18726         
18727         if(this.triggerAction == 'all') {
18728             this.doQuery(this.allQuery, true);
18729         } else {
18730             this.doQuery(this.getRawValue());
18731         }
18732     },
18733     
18734     onSearchFieldClick : function(e)
18735     {
18736         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18737             this.onTickableFooterButtonClick(e, false, false);
18738             return;
18739         }
18740         
18741         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18742             return;
18743         }
18744         
18745         this.page = 0;
18746         this.loadNext = false;
18747         this.hasFocus = true;
18748         
18749         if(this.triggerAction == 'all') {
18750             this.doQuery(this.allQuery, true);
18751         } else {
18752             this.doQuery(this.getRawValue());
18753         }
18754     },
18755     
18756     listKeyPress : function(e)
18757     {
18758         //Roo.log('listkeypress');
18759         // scroll to first matching element based on key pres..
18760         if (e.isSpecialKey()) {
18761             return false;
18762         }
18763         var k = String.fromCharCode(e.getKey()).toUpperCase();
18764         //Roo.log(k);
18765         var match  = false;
18766         var csel = this.view.getSelectedNodes();
18767         var cselitem = false;
18768         if (csel.length) {
18769             var ix = this.view.indexOf(csel[0]);
18770             cselitem  = this.store.getAt(ix);
18771             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18772                 cselitem = false;
18773             }
18774             
18775         }
18776         
18777         this.store.each(function(v) { 
18778             if (cselitem) {
18779                 // start at existing selection.
18780                 if (cselitem.id == v.id) {
18781                     cselitem = false;
18782                 }
18783                 return true;
18784             }
18785                 
18786             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18787                 match = this.store.indexOf(v);
18788                 return false;
18789             }
18790             return true;
18791         }, this);
18792         
18793         if (match === false) {
18794             return true; // no more action?
18795         }
18796         // scroll to?
18797         this.view.select(match);
18798         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18799         sn.scrollIntoView(sn.dom.parentNode, false);
18800     },
18801     
18802     onViewScroll : function(e, t){
18803         
18804         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){
18805             return;
18806         }
18807         
18808         this.hasQuery = true;
18809         
18810         this.loading = this.list.select('.loading', true).first();
18811         
18812         if(this.loading === null){
18813             this.list.createChild({
18814                 tag: 'div',
18815                 cls: 'loading roo-select2-more-results roo-select2-active',
18816                 html: 'Loading more results...'
18817             });
18818             
18819             this.loading = this.list.select('.loading', true).first();
18820             
18821             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18822             
18823             this.loading.hide();
18824         }
18825         
18826         this.loading.show();
18827         
18828         var _combo = this;
18829         
18830         this.page++;
18831         this.loadNext = true;
18832         
18833         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18834         
18835         return;
18836     },
18837     
18838     addItem : function(o)
18839     {   
18840         var dv = ''; // display value
18841         
18842         if (this.displayField) {
18843             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18844         } else {
18845             // this is an error condition!!!
18846             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18847         }
18848         
18849         if(!dv.length){
18850             return;
18851         }
18852         
18853         var choice = this.choices.createChild({
18854             tag: 'li',
18855             cls: 'roo-select2-search-choice',
18856             cn: [
18857                 {
18858                     tag: 'div',
18859                     html: dv
18860                 },
18861                 {
18862                     tag: 'a',
18863                     href: '#',
18864                     cls: 'roo-select2-search-choice-close fa fa-times',
18865                     tabindex: '-1'
18866                 }
18867             ]
18868             
18869         }, this.searchField);
18870         
18871         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18872         
18873         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18874         
18875         this.item.push(o);
18876         
18877         this.lastData = o;
18878         
18879         this.syncValue();
18880         
18881         this.inputEl().dom.value = '';
18882         
18883         this.validate();
18884     },
18885     
18886     onRemoveItem : function(e, _self, o)
18887     {
18888         e.preventDefault();
18889         
18890         this.lastItem = Roo.apply([], this.item);
18891         
18892         var index = this.item.indexOf(o.data) * 1;
18893         
18894         if( index < 0){
18895             Roo.log('not this item?!');
18896             return;
18897         }
18898         
18899         this.item.splice(index, 1);
18900         o.item.remove();
18901         
18902         this.syncValue();
18903         
18904         this.fireEvent('remove', this, e);
18905         
18906         this.validate();
18907         
18908     },
18909     
18910     syncValue : function()
18911     {
18912         if(!this.item.length){
18913             this.clearValue();
18914             return;
18915         }
18916             
18917         var value = [];
18918         var _this = this;
18919         Roo.each(this.item, function(i){
18920             if(_this.valueField){
18921                 value.push(i[_this.valueField]);
18922                 return;
18923             }
18924
18925             value.push(i);
18926         });
18927
18928         this.value = value.join(',');
18929
18930         if(this.hiddenField){
18931             this.hiddenField.dom.value = this.value;
18932         }
18933         
18934         this.store.fireEvent("datachanged", this.store);
18935         
18936         this.validate();
18937     },
18938     
18939     clearItem : function()
18940     {
18941         if(!this.multiple){
18942             return;
18943         }
18944         
18945         this.item = [];
18946         
18947         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18948            c.remove();
18949         });
18950         
18951         this.syncValue();
18952         
18953         this.validate();
18954         
18955         if(this.tickable && !Roo.isTouch){
18956             this.view.refresh();
18957         }
18958     },
18959     
18960     inputEl: function ()
18961     {
18962         if(Roo.isIOS && this.useNativeIOS){
18963             return this.el.select('select.roo-ios-select', true).first();
18964         }
18965         
18966         if(Roo.isTouch && this.mobileTouchView){
18967             return this.el.select('input.form-control',true).first();
18968         }
18969         
18970         if(this.tickable){
18971             return this.searchField;
18972         }
18973         
18974         return this.el.select('input.form-control',true).first();
18975     },
18976     
18977     onTickableFooterButtonClick : function(e, btn, el)
18978     {
18979         e.preventDefault();
18980         
18981         this.lastItem = Roo.apply([], this.item);
18982         
18983         if(btn && btn.name == 'cancel'){
18984             this.tickItems = Roo.apply([], this.item);
18985             this.collapse();
18986             return;
18987         }
18988         
18989         this.clearItem();
18990         
18991         var _this = this;
18992         
18993         Roo.each(this.tickItems, function(o){
18994             _this.addItem(o);
18995         });
18996         
18997         this.collapse();
18998         
18999     },
19000     
19001     validate : function()
19002     {
19003         if(this.getVisibilityEl().hasClass('hidden')){
19004             return true;
19005         }
19006         
19007         var v = this.getRawValue();
19008         
19009         if(this.multiple){
19010             v = this.getValue();
19011         }
19012         
19013         if(this.disabled || this.allowBlank || v.length){
19014             this.markValid();
19015             return true;
19016         }
19017         
19018         this.markInvalid();
19019         return false;
19020     },
19021     
19022     tickableInputEl : function()
19023     {
19024         if(!this.tickable || !this.editable){
19025             return this.inputEl();
19026         }
19027         
19028         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19029     },
19030     
19031     
19032     getAutoCreateTouchView : function()
19033     {
19034         var id = Roo.id();
19035         
19036         var cfg = {
19037             cls: 'form-group' //input-group
19038         };
19039         
19040         var input =  {
19041             tag: 'input',
19042             id : id,
19043             type : this.inputType,
19044             cls : 'form-control x-combo-noedit',
19045             autocomplete: 'new-password',
19046             placeholder : this.placeholder || '',
19047             readonly : true
19048         };
19049         
19050         if (this.name) {
19051             input.name = this.name;
19052         }
19053         
19054         if (this.size) {
19055             input.cls += ' input-' + this.size;
19056         }
19057         
19058         if (this.disabled) {
19059             input.disabled = true;
19060         }
19061         
19062         var inputblock = {
19063             cls : 'roo-combobox-wrap',
19064             cn : [
19065                 input
19066             ]
19067         };
19068         
19069         if(this.before){
19070             inputblock.cls += ' input-group';
19071             
19072             inputblock.cn.unshift({
19073                 tag :'span',
19074                 cls : 'input-group-addon input-group-prepend input-group-text',
19075                 html : this.before
19076             });
19077         }
19078         
19079         if(this.removable && !this.multiple){
19080             inputblock.cls += ' roo-removable';
19081             
19082             inputblock.cn.push({
19083                 tag: 'button',
19084                 html : 'x',
19085                 cls : 'roo-combo-removable-btn close'
19086             });
19087         }
19088
19089         if(this.hasFeedback && !this.allowBlank){
19090             
19091             inputblock.cls += ' has-feedback';
19092             
19093             inputblock.cn.push({
19094                 tag: 'span',
19095                 cls: 'glyphicon form-control-feedback'
19096             });
19097             
19098         }
19099         
19100         if (this.after) {
19101             
19102             inputblock.cls += (this.before) ? '' : ' input-group';
19103             
19104             inputblock.cn.push({
19105                 tag :'span',
19106                 cls : 'input-group-addon input-group-append input-group-text',
19107                 html : this.after
19108             });
19109         }
19110
19111         
19112         var ibwrap = inputblock;
19113         
19114         if(this.multiple){
19115             ibwrap = {
19116                 tag: 'ul',
19117                 cls: 'roo-select2-choices',
19118                 cn:[
19119                     {
19120                         tag: 'li',
19121                         cls: 'roo-select2-search-field',
19122                         cn: [
19123
19124                             inputblock
19125                         ]
19126                     }
19127                 ]
19128             };
19129         
19130             
19131         }
19132         
19133         var combobox = {
19134             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19135             cn: [
19136                 {
19137                     tag: 'input',
19138                     type : 'hidden',
19139                     cls: 'form-hidden-field'
19140                 },
19141                 ibwrap
19142             ]
19143         };
19144         
19145         if(!this.multiple && this.showToggleBtn){
19146             
19147             var caret = {
19148                 cls: 'caret'
19149             };
19150             
19151             if (this.caret != false) {
19152                 caret = {
19153                      tag: 'i',
19154                      cls: 'fa fa-' + this.caret
19155                 };
19156                 
19157             }
19158             
19159             combobox.cn.push({
19160                 tag :'span',
19161                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19162                 cn : [
19163                     Roo.bootstrap.version == 3 ? caret : '',
19164                     {
19165                         tag: 'span',
19166                         cls: 'combobox-clear',
19167                         cn  : [
19168                             {
19169                                 tag : 'i',
19170                                 cls: 'icon-remove'
19171                             }
19172                         ]
19173                     }
19174                 ]
19175
19176             })
19177         }
19178         
19179         if(this.multiple){
19180             combobox.cls += ' roo-select2-container-multi';
19181         }
19182         
19183         var required =  this.allowBlank ?  {
19184                     tag : 'i',
19185                     style: 'display: none'
19186                 } : {
19187                    tag : 'i',
19188                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19189                    tooltip : 'This field is required'
19190                 };
19191         
19192         var align = this.labelAlign || this.parentLabelAlign();
19193         
19194         if (align ==='left' && this.fieldLabel.length) {
19195
19196             cfg.cn = [
19197                 required,
19198                 {
19199                     tag: 'label',
19200                     cls : 'control-label col-form-label',
19201                     html : this.fieldLabel
19202
19203                 },
19204                 {
19205                     cls : 'roo-combobox-wrap ', 
19206                     cn: [
19207                         combobox
19208                     ]
19209                 }
19210             ];
19211             
19212             var labelCfg = cfg.cn[1];
19213             var contentCfg = cfg.cn[2];
19214             
19215
19216             if(this.indicatorpos == 'right'){
19217                 cfg.cn = [
19218                     {
19219                         tag: 'label',
19220                         'for' :  id,
19221                         cls : 'control-label col-form-label',
19222                         cn : [
19223                             {
19224                                 tag : 'span',
19225                                 html : this.fieldLabel
19226                             },
19227                             required
19228                         ]
19229                     },
19230                     {
19231                         cls : "roo-combobox-wrap ",
19232                         cn: [
19233                             combobox
19234                         ]
19235                     }
19236
19237                 ];
19238                 
19239                 labelCfg = cfg.cn[0];
19240                 contentCfg = cfg.cn[1];
19241             }
19242             
19243            
19244             
19245             if(this.labelWidth > 12){
19246                 labelCfg.style = "width: " + this.labelWidth + 'px';
19247             }
19248            
19249             if(this.labelWidth < 13 && this.labelmd == 0){
19250                 this.labelmd = this.labelWidth;
19251             }
19252             
19253             if(this.labellg > 0){
19254                 labelCfg.cls += ' col-lg-' + this.labellg;
19255                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19256             }
19257             
19258             if(this.labelmd > 0){
19259                 labelCfg.cls += ' col-md-' + this.labelmd;
19260                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19261             }
19262             
19263             if(this.labelsm > 0){
19264                 labelCfg.cls += ' col-sm-' + this.labelsm;
19265                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19266             }
19267             
19268             if(this.labelxs > 0){
19269                 labelCfg.cls += ' col-xs-' + this.labelxs;
19270                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19271             }
19272                 
19273                 
19274         } else if ( this.fieldLabel.length) {
19275             cfg.cn = [
19276                required,
19277                 {
19278                     tag: 'label',
19279                     cls : 'control-label',
19280                     html : this.fieldLabel
19281
19282                 },
19283                 {
19284                     cls : '', 
19285                     cn: [
19286                         combobox
19287                     ]
19288                 }
19289             ];
19290             
19291             if(this.indicatorpos == 'right'){
19292                 cfg.cn = [
19293                     {
19294                         tag: 'label',
19295                         cls : 'control-label',
19296                         html : this.fieldLabel,
19297                         cn : [
19298                             required
19299                         ]
19300                     },
19301                     {
19302                         cls : '', 
19303                         cn: [
19304                             combobox
19305                         ]
19306                     }
19307                 ];
19308             }
19309         } else {
19310             cfg.cn = combobox;    
19311         }
19312         
19313         
19314         var settings = this;
19315         
19316         ['xs','sm','md','lg'].map(function(size){
19317             if (settings[size]) {
19318                 cfg.cls += ' col-' + size + '-' + settings[size];
19319             }
19320         });
19321         
19322         return cfg;
19323     },
19324     
19325     initTouchView : function()
19326     {
19327         this.renderTouchView();
19328         
19329         this.touchViewEl.on('scroll', function(){
19330             this.el.dom.scrollTop = 0;
19331         }, this);
19332         
19333         this.originalValue = this.getValue();
19334         
19335         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19336         
19337         this.inputEl().on("click", this.showTouchView, this);
19338         if (this.triggerEl) {
19339             this.triggerEl.on("click", this.showTouchView, this);
19340         }
19341         
19342         
19343         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19344         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19345         
19346         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19347         
19348         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19349         this.store.on('load', this.onTouchViewLoad, this);
19350         this.store.on('loadexception', this.onTouchViewLoadException, this);
19351         
19352         if(this.hiddenName){
19353             
19354             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19355             
19356             this.hiddenField.dom.value =
19357                 this.hiddenValue !== undefined ? this.hiddenValue :
19358                 this.value !== undefined ? this.value : '';
19359         
19360             this.el.dom.removeAttribute('name');
19361             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19362         }
19363         
19364         if(this.multiple){
19365             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19366             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19367         }
19368         
19369         if(this.removable && !this.multiple){
19370             var close = this.closeTriggerEl();
19371             if(close){
19372                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19373                 close.on('click', this.removeBtnClick, this, close);
19374             }
19375         }
19376         /*
19377          * fix the bug in Safari iOS8
19378          */
19379         this.inputEl().on("focus", function(e){
19380             document.activeElement.blur();
19381         }, this);
19382         
19383         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19384         
19385         return;
19386         
19387         
19388     },
19389     
19390     renderTouchView : function()
19391     {
19392         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19393         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19394         
19395         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19396         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19397         
19398         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19399         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19400         this.touchViewBodyEl.setStyle('overflow', 'auto');
19401         
19402         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19403         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19404         
19405         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19406         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19407         
19408     },
19409     
19410     showTouchView : function()
19411     {
19412         if(this.disabled){
19413             return;
19414         }
19415         
19416         this.touchViewHeaderEl.hide();
19417
19418         if(this.modalTitle.length){
19419             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19420             this.touchViewHeaderEl.show();
19421         }
19422
19423         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19424         this.touchViewEl.show();
19425
19426         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19427         
19428         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19429         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19430
19431         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19432
19433         if(this.modalTitle.length){
19434             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19435         }
19436         
19437         this.touchViewBodyEl.setHeight(bodyHeight);
19438
19439         if(this.animate){
19440             var _this = this;
19441             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19442         }else{
19443             this.touchViewEl.addClass(['in','show']);
19444         }
19445         
19446         if(this._touchViewMask){
19447             Roo.get(document.body).addClass("x-body-masked");
19448             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19449             this._touchViewMask.setStyle('z-index', 10000);
19450             this._touchViewMask.addClass('show');
19451         }
19452         
19453         this.doTouchViewQuery();
19454         
19455     },
19456     
19457     hideTouchView : function()
19458     {
19459         this.touchViewEl.removeClass(['in','show']);
19460
19461         if(this.animate){
19462             var _this = this;
19463             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19464         }else{
19465             this.touchViewEl.setStyle('display', 'none');
19466         }
19467         
19468         if(this._touchViewMask){
19469             this._touchViewMask.removeClass('show');
19470             Roo.get(document.body).removeClass("x-body-masked");
19471         }
19472     },
19473     
19474     setTouchViewValue : function()
19475     {
19476         if(this.multiple){
19477             this.clearItem();
19478         
19479             var _this = this;
19480
19481             Roo.each(this.tickItems, function(o){
19482                 this.addItem(o);
19483             }, this);
19484         }
19485         
19486         this.hideTouchView();
19487     },
19488     
19489     doTouchViewQuery : function()
19490     {
19491         var qe = {
19492             query: '',
19493             forceAll: true,
19494             combo: this,
19495             cancel:false
19496         };
19497         
19498         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19499             return false;
19500         }
19501         
19502         if(!this.alwaysQuery || this.mode == 'local'){
19503             this.onTouchViewLoad();
19504             return;
19505         }
19506         
19507         this.store.load();
19508     },
19509     
19510     onTouchViewBeforeLoad : function(combo,opts)
19511     {
19512         return;
19513     },
19514
19515     // private
19516     onTouchViewLoad : function()
19517     {
19518         if(this.store.getCount() < 1){
19519             this.onTouchViewEmptyResults();
19520             return;
19521         }
19522         
19523         this.clearTouchView();
19524         
19525         var rawValue = this.getRawValue();
19526         
19527         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19528         
19529         this.tickItems = [];
19530         
19531         this.store.data.each(function(d, rowIndex){
19532             var row = this.touchViewListGroup.createChild(template);
19533             
19534             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19535                 row.addClass(d.data.cls);
19536             }
19537             
19538             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19539                 var cfg = {
19540                     data : d.data,
19541                     html : d.data[this.displayField]
19542                 };
19543                 
19544                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19545                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19546                 }
19547             }
19548             row.removeClass('selected');
19549             if(!this.multiple && this.valueField &&
19550                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19551             {
19552                 // radio buttons..
19553                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19554                 row.addClass('selected');
19555             }
19556             
19557             if(this.multiple && this.valueField &&
19558                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19559             {
19560                 
19561                 // checkboxes...
19562                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19563                 this.tickItems.push(d.data);
19564             }
19565             
19566             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19567             
19568         }, this);
19569         
19570         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19571         
19572         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19573
19574         if(this.modalTitle.length){
19575             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19576         }
19577
19578         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19579         
19580         if(this.mobile_restrict_height && listHeight < bodyHeight){
19581             this.touchViewBodyEl.setHeight(listHeight);
19582         }
19583         
19584         var _this = this;
19585         
19586         if(firstChecked && listHeight > bodyHeight){
19587             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19588         }
19589         
19590     },
19591     
19592     onTouchViewLoadException : function()
19593     {
19594         this.hideTouchView();
19595     },
19596     
19597     onTouchViewEmptyResults : function()
19598     {
19599         this.clearTouchView();
19600         
19601         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19602         
19603         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19604         
19605     },
19606     
19607     clearTouchView : function()
19608     {
19609         this.touchViewListGroup.dom.innerHTML = '';
19610     },
19611     
19612     onTouchViewClick : function(e, el, o)
19613     {
19614         e.preventDefault();
19615         
19616         var row = o.row;
19617         var rowIndex = o.rowIndex;
19618         
19619         var r = this.store.getAt(rowIndex);
19620         
19621         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19622             
19623             if(!this.multiple){
19624                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19625                     c.dom.removeAttribute('checked');
19626                 }, this);
19627
19628                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19629
19630                 this.setFromData(r.data);
19631
19632                 var close = this.closeTriggerEl();
19633
19634                 if(close){
19635                     close.show();
19636                 }
19637
19638                 this.hideTouchView();
19639
19640                 this.fireEvent('select', this, r, rowIndex);
19641
19642                 return;
19643             }
19644
19645             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19646                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19647                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19648                 return;
19649             }
19650
19651             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19652             this.addItem(r.data);
19653             this.tickItems.push(r.data);
19654         }
19655     },
19656     
19657     getAutoCreateNativeIOS : function()
19658     {
19659         var cfg = {
19660             cls: 'form-group' //input-group,
19661         };
19662         
19663         var combobox =  {
19664             tag: 'select',
19665             cls : 'roo-ios-select'
19666         };
19667         
19668         if (this.name) {
19669             combobox.name = this.name;
19670         }
19671         
19672         if (this.disabled) {
19673             combobox.disabled = true;
19674         }
19675         
19676         var settings = this;
19677         
19678         ['xs','sm','md','lg'].map(function(size){
19679             if (settings[size]) {
19680                 cfg.cls += ' col-' + size + '-' + settings[size];
19681             }
19682         });
19683         
19684         cfg.cn = combobox;
19685         
19686         return cfg;
19687         
19688     },
19689     
19690     initIOSView : function()
19691     {
19692         this.store.on('load', this.onIOSViewLoad, this);
19693         
19694         return;
19695     },
19696     
19697     onIOSViewLoad : function()
19698     {
19699         if(this.store.getCount() < 1){
19700             return;
19701         }
19702         
19703         this.clearIOSView();
19704         
19705         if(this.allowBlank) {
19706             
19707             var default_text = '-- SELECT --';
19708             
19709             if(this.placeholder.length){
19710                 default_text = this.placeholder;
19711             }
19712             
19713             if(this.emptyTitle.length){
19714                 default_text += ' - ' + this.emptyTitle + ' -';
19715             }
19716             
19717             var opt = this.inputEl().createChild({
19718                 tag: 'option',
19719                 value : 0,
19720                 html : default_text
19721             });
19722             
19723             var o = {};
19724             o[this.valueField] = 0;
19725             o[this.displayField] = default_text;
19726             
19727             this.ios_options.push({
19728                 data : o,
19729                 el : opt
19730             });
19731             
19732         }
19733         
19734         this.store.data.each(function(d, rowIndex){
19735             
19736             var html = '';
19737             
19738             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19739                 html = d.data[this.displayField];
19740             }
19741             
19742             var value = '';
19743             
19744             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19745                 value = d.data[this.valueField];
19746             }
19747             
19748             var option = {
19749                 tag: 'option',
19750                 value : value,
19751                 html : html
19752             };
19753             
19754             if(this.value == d.data[this.valueField]){
19755                 option['selected'] = true;
19756             }
19757             
19758             var opt = this.inputEl().createChild(option);
19759             
19760             this.ios_options.push({
19761                 data : d.data,
19762                 el : opt
19763             });
19764             
19765         }, this);
19766         
19767         this.inputEl().on('change', function(){
19768            this.fireEvent('select', this);
19769         }, this);
19770         
19771     },
19772     
19773     clearIOSView: function()
19774     {
19775         this.inputEl().dom.innerHTML = '';
19776         
19777         this.ios_options = [];
19778     },
19779     
19780     setIOSValue: function(v)
19781     {
19782         this.value = v;
19783         
19784         if(!this.ios_options){
19785             return;
19786         }
19787         
19788         Roo.each(this.ios_options, function(opts){
19789            
19790            opts.el.dom.removeAttribute('selected');
19791            
19792            if(opts.data[this.valueField] != v){
19793                return;
19794            }
19795            
19796            opts.el.dom.setAttribute('selected', true);
19797            
19798         }, this);
19799     }
19800
19801     /** 
19802     * @cfg {Boolean} grow 
19803     * @hide 
19804     */
19805     /** 
19806     * @cfg {Number} growMin 
19807     * @hide 
19808     */
19809     /** 
19810     * @cfg {Number} growMax 
19811     * @hide 
19812     */
19813     /**
19814      * @hide
19815      * @method autoSize
19816      */
19817 });
19818
19819 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19820     
19821     header : {
19822         tag: 'div',
19823         cls: 'modal-header',
19824         cn: [
19825             {
19826                 tag: 'h4',
19827                 cls: 'modal-title'
19828             }
19829         ]
19830     },
19831     
19832     body : {
19833         tag: 'div',
19834         cls: 'modal-body',
19835         cn: [
19836             {
19837                 tag: 'ul',
19838                 cls: 'list-group'
19839             }
19840         ]
19841     },
19842     
19843     listItemRadio : {
19844         tag: 'li',
19845         cls: 'list-group-item',
19846         cn: [
19847             {
19848                 tag: 'span',
19849                 cls: 'roo-combobox-list-group-item-value'
19850             },
19851             {
19852                 tag: 'div',
19853                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19854                 cn: [
19855                     {
19856                         tag: 'input',
19857                         type: 'radio'
19858                     },
19859                     {
19860                         tag: 'label'
19861                     }
19862                 ]
19863             }
19864         ]
19865     },
19866     
19867     listItemCheckbox : {
19868         tag: 'li',
19869         cls: 'list-group-item',
19870         cn: [
19871             {
19872                 tag: 'span',
19873                 cls: 'roo-combobox-list-group-item-value'
19874             },
19875             {
19876                 tag: 'div',
19877                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19878                 cn: [
19879                     {
19880                         tag: 'input',
19881                         type: 'checkbox'
19882                     },
19883                     {
19884                         tag: 'label'
19885                     }
19886                 ]
19887             }
19888         ]
19889     },
19890     
19891     emptyResult : {
19892         tag: 'div',
19893         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19894     },
19895     
19896     footer : {
19897         tag: 'div',
19898         cls: 'modal-footer',
19899         cn: [
19900             {
19901                 tag: 'div',
19902                 cls: 'row',
19903                 cn: [
19904                     {
19905                         tag: 'div',
19906                         cls: 'col-xs-6 text-left',
19907                         cn: {
19908                             tag: 'button',
19909                             cls: 'btn btn-danger roo-touch-view-cancel',
19910                             html: 'Cancel'
19911                         }
19912                     },
19913                     {
19914                         tag: 'div',
19915                         cls: 'col-xs-6 text-right',
19916                         cn: {
19917                             tag: 'button',
19918                             cls: 'btn btn-success roo-touch-view-ok',
19919                             html: 'OK'
19920                         }
19921                     }
19922                 ]
19923             }
19924         ]
19925         
19926     }
19927 });
19928
19929 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19930     
19931     touchViewTemplate : {
19932         tag: 'div',
19933         cls: 'modal fade roo-combobox-touch-view',
19934         cn: [
19935             {
19936                 tag: 'div',
19937                 cls: 'modal-dialog',
19938                 style : 'position:fixed', // we have to fix position....
19939                 cn: [
19940                     {
19941                         tag: 'div',
19942                         cls: 'modal-content',
19943                         cn: [
19944                             Roo.bootstrap.form.ComboBox.header,
19945                             Roo.bootstrap.form.ComboBox.body,
19946                             Roo.bootstrap.form.ComboBox.footer
19947                         ]
19948                     }
19949                 ]
19950             }
19951         ]
19952     }
19953 });/*
19954  * Based on:
19955  * Ext JS Library 1.1.1
19956  * Copyright(c) 2006-2007, Ext JS, LLC.
19957  *
19958  * Originally Released Under LGPL - original licence link has changed is not relivant.
19959  *
19960  * Fork - LGPL
19961  * <script type="text/javascript">
19962  */
19963
19964 /**
19965  * @class Roo.View
19966  * @extends Roo.util.Observable
19967  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19968  * This class also supports single and multi selection modes. <br>
19969  * Create a data model bound view:
19970  <pre><code>
19971  var store = new Roo.data.Store(...);
19972
19973  var view = new Roo.View({
19974     el : "my-element",
19975     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19976  
19977     singleSelect: true,
19978     selectedClass: "ydataview-selected",
19979     store: store
19980  });
19981
19982  // listen for node click?
19983  view.on("click", function(vw, index, node, e){
19984  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19985  });
19986
19987  // load XML data
19988  dataModel.load("foobar.xml");
19989  </code></pre>
19990  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19991  * <br><br>
19992  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19993  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19994  * 
19995  * Note: old style constructor is still suported (container, template, config)
19996  * 
19997  * @constructor
19998  * Create a new View
19999  * @param {Object} config The config object
20000  * 
20001  */
20002 Roo.View = function(config, depreciated_tpl, depreciated_config){
20003     
20004     this.parent = false;
20005     
20006     if (typeof(depreciated_tpl) == 'undefined') {
20007         // new way.. - universal constructor.
20008         Roo.apply(this, config);
20009         this.el  = Roo.get(this.el);
20010     } else {
20011         // old format..
20012         this.el  = Roo.get(config);
20013         this.tpl = depreciated_tpl;
20014         Roo.apply(this, depreciated_config);
20015     }
20016     this.wrapEl  = this.el.wrap().wrap();
20017     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20018     
20019     
20020     if(typeof(this.tpl) == "string"){
20021         this.tpl = new Roo.Template(this.tpl);
20022     } else {
20023         // support xtype ctors..
20024         this.tpl = new Roo.factory(this.tpl, Roo);
20025     }
20026     
20027     
20028     this.tpl.compile();
20029     
20030     /** @private */
20031     this.addEvents({
20032         /**
20033          * @event beforeclick
20034          * Fires before a click is processed. Returns false to cancel the default action.
20035          * @param {Roo.View} this
20036          * @param {Number} index The index of the target node
20037          * @param {HTMLElement} node The target node
20038          * @param {Roo.EventObject} e The raw event object
20039          */
20040             "beforeclick" : true,
20041         /**
20042          * @event click
20043          * Fires when a template node is clicked.
20044          * @param {Roo.View} this
20045          * @param {Number} index The index of the target node
20046          * @param {HTMLElement} node The target node
20047          * @param {Roo.EventObject} e The raw event object
20048          */
20049             "click" : true,
20050         /**
20051          * @event dblclick
20052          * Fires when a template node is double clicked.
20053          * @param {Roo.View} this
20054          * @param {Number} index The index of the target node
20055          * @param {HTMLElement} node The target node
20056          * @param {Roo.EventObject} e The raw event object
20057          */
20058             "dblclick" : true,
20059         /**
20060          * @event contextmenu
20061          * Fires when a template node is right clicked.
20062          * @param {Roo.View} this
20063          * @param {Number} index The index of the target node
20064          * @param {HTMLElement} node The target node
20065          * @param {Roo.EventObject} e The raw event object
20066          */
20067             "contextmenu" : true,
20068         /**
20069          * @event selectionchange
20070          * Fires when the selected nodes change.
20071          * @param {Roo.View} this
20072          * @param {Array} selections Array of the selected nodes
20073          */
20074             "selectionchange" : true,
20075     
20076         /**
20077          * @event beforeselect
20078          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20079          * @param {Roo.View} this
20080          * @param {HTMLElement} node The node to be selected
20081          * @param {Array} selections Array of currently selected nodes
20082          */
20083             "beforeselect" : true,
20084         /**
20085          * @event preparedata
20086          * Fires on every row to render, to allow you to change the data.
20087          * @param {Roo.View} this
20088          * @param {Object} data to be rendered (change this)
20089          */
20090           "preparedata" : true
20091           
20092           
20093         });
20094
20095
20096
20097     this.el.on({
20098         "click": this.onClick,
20099         "dblclick": this.onDblClick,
20100         "contextmenu": this.onContextMenu,
20101         scope:this
20102     });
20103
20104     this.selections = [];
20105     this.nodes = [];
20106     this.cmp = new Roo.CompositeElementLite([]);
20107     if(this.store){
20108         this.store = Roo.factory(this.store, Roo.data);
20109         this.setStore(this.store, true);
20110     }
20111     
20112     if ( this.footer && this.footer.xtype) {
20113            
20114          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20115         
20116         this.footer.dataSource = this.store;
20117         this.footer.container = fctr;
20118         this.footer = Roo.factory(this.footer, Roo);
20119         fctr.insertFirst(this.el);
20120         
20121         // this is a bit insane - as the paging toolbar seems to detach the el..
20122 //        dom.parentNode.parentNode.parentNode
20123          // they get detached?
20124     }
20125     
20126     
20127     Roo.View.superclass.constructor.call(this);
20128     
20129     
20130 };
20131
20132 Roo.extend(Roo.View, Roo.util.Observable, {
20133     
20134      /**
20135      * @cfg {Roo.data.Store} store Data store to load data from.
20136      */
20137     store : false,
20138     
20139     /**
20140      * @cfg {String|Roo.Element} el The container element.
20141      */
20142     el : '',
20143     
20144     /**
20145      * @cfg {String|Roo.Template} tpl The template used by this View 
20146      */
20147     tpl : false,
20148     /**
20149      * @cfg {String} dataName the named area of the template to use as the data area
20150      *                          Works with domtemplates roo-name="name"
20151      */
20152     dataName: false,
20153     /**
20154      * @cfg {String} selectedClass The css class to add to selected nodes
20155      */
20156     selectedClass : "x-view-selected",
20157      /**
20158      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20159      */
20160     emptyText : "",
20161     
20162     /**
20163      * @cfg {String} text to display on mask (default Loading)
20164      */
20165     mask : false,
20166     /**
20167      * @cfg {Boolean} multiSelect Allow multiple selection
20168      */
20169     multiSelect : false,
20170     /**
20171      * @cfg {Boolean} singleSelect Allow single selection
20172      */
20173     singleSelect:  false,
20174     
20175     /**
20176      * @cfg {Boolean} toggleSelect - selecting 
20177      */
20178     toggleSelect : false,
20179     
20180     /**
20181      * @cfg {Boolean} tickable - selecting 
20182      */
20183     tickable : false,
20184     
20185     /**
20186      * Returns the element this view is bound to.
20187      * @return {Roo.Element}
20188      */
20189     getEl : function(){
20190         return this.wrapEl;
20191     },
20192     
20193     
20194
20195     /**
20196      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20197      */
20198     refresh : function(){
20199         //Roo.log('refresh');
20200         var t = this.tpl;
20201         
20202         // if we are using something like 'domtemplate', then
20203         // the what gets used is:
20204         // t.applySubtemplate(NAME, data, wrapping data..)
20205         // the outer template then get' applied with
20206         //     the store 'extra data'
20207         // and the body get's added to the
20208         //      roo-name="data" node?
20209         //      <span class='roo-tpl-{name}'></span> ?????
20210         
20211         
20212         
20213         this.clearSelections();
20214         this.el.update("");
20215         var html = [];
20216         var records = this.store.getRange();
20217         if(records.length < 1) {
20218             
20219             // is this valid??  = should it render a template??
20220             
20221             this.el.update(this.emptyText);
20222             return;
20223         }
20224         var el = this.el;
20225         if (this.dataName) {
20226             this.el.update(t.apply(this.store.meta)); //????
20227             el = this.el.child('.roo-tpl-' + this.dataName);
20228         }
20229         
20230         for(var i = 0, len = records.length; i < len; i++){
20231             var data = this.prepareData(records[i].data, i, records[i]);
20232             this.fireEvent("preparedata", this, data, i, records[i]);
20233             
20234             var d = Roo.apply({}, data);
20235             
20236             if(this.tickable){
20237                 Roo.apply(d, {'roo-id' : Roo.id()});
20238                 
20239                 var _this = this;
20240             
20241                 Roo.each(this.parent.item, function(item){
20242                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20243                         return;
20244                     }
20245                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20246                 });
20247             }
20248             
20249             html[html.length] = Roo.util.Format.trim(
20250                 this.dataName ?
20251                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20252                     t.apply(d)
20253             );
20254         }
20255         
20256         
20257         
20258         el.update(html.join(""));
20259         this.nodes = el.dom.childNodes;
20260         this.updateIndexes(0);
20261     },
20262     
20263
20264     /**
20265      * Function to override to reformat the data that is sent to
20266      * the template for each node.
20267      * DEPRICATED - use the preparedata event handler.
20268      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20269      * a JSON object for an UpdateManager bound view).
20270      */
20271     prepareData : function(data, index, record)
20272     {
20273         this.fireEvent("preparedata", this, data, index, record);
20274         return data;
20275     },
20276
20277     onUpdate : function(ds, record){
20278         // Roo.log('on update');   
20279         this.clearSelections();
20280         var index = this.store.indexOf(record);
20281         var n = this.nodes[index];
20282         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20283         n.parentNode.removeChild(n);
20284         this.updateIndexes(index, index);
20285     },
20286
20287     
20288     
20289 // --------- FIXME     
20290     onAdd : function(ds, records, index)
20291     {
20292         //Roo.log(['on Add', ds, records, index] );        
20293         this.clearSelections();
20294         if(this.nodes.length == 0){
20295             this.refresh();
20296             return;
20297         }
20298         var n = this.nodes[index];
20299         for(var i = 0, len = records.length; i < len; i++){
20300             var d = this.prepareData(records[i].data, i, records[i]);
20301             if(n){
20302                 this.tpl.insertBefore(n, d);
20303             }else{
20304                 
20305                 this.tpl.append(this.el, d);
20306             }
20307         }
20308         this.updateIndexes(index);
20309     },
20310
20311     onRemove : function(ds, record, index){
20312        // Roo.log('onRemove');
20313         this.clearSelections();
20314         var el = this.dataName  ?
20315             this.el.child('.roo-tpl-' + this.dataName) :
20316             this.el; 
20317         
20318         el.dom.removeChild(this.nodes[index]);
20319         this.updateIndexes(index);
20320     },
20321
20322     /**
20323      * Refresh an individual node.
20324      * @param {Number} index
20325      */
20326     refreshNode : function(index){
20327         this.onUpdate(this.store, this.store.getAt(index));
20328     },
20329
20330     updateIndexes : function(startIndex, endIndex){
20331         var ns = this.nodes;
20332         startIndex = startIndex || 0;
20333         endIndex = endIndex || ns.length - 1;
20334         for(var i = startIndex; i <= endIndex; i++){
20335             ns[i].nodeIndex = i;
20336         }
20337     },
20338
20339     /**
20340      * Changes the data store this view uses and refresh the view.
20341      * @param {Store} store
20342      */
20343     setStore : function(store, initial){
20344         if(!initial && this.store){
20345             this.store.un("datachanged", this.refresh);
20346             this.store.un("add", this.onAdd);
20347             this.store.un("remove", this.onRemove);
20348             this.store.un("update", this.onUpdate);
20349             this.store.un("clear", this.refresh);
20350             this.store.un("beforeload", this.onBeforeLoad);
20351             this.store.un("load", this.onLoad);
20352             this.store.un("loadexception", this.onLoad);
20353         }
20354         if(store){
20355           
20356             store.on("datachanged", this.refresh, this);
20357             store.on("add", this.onAdd, this);
20358             store.on("remove", this.onRemove, this);
20359             store.on("update", this.onUpdate, this);
20360             store.on("clear", this.refresh, this);
20361             store.on("beforeload", this.onBeforeLoad, this);
20362             store.on("load", this.onLoad, this);
20363             store.on("loadexception", this.onLoad, this);
20364         }
20365         
20366         if(store){
20367             this.refresh();
20368         }
20369     },
20370     /**
20371      * onbeforeLoad - masks the loading area.
20372      *
20373      */
20374     onBeforeLoad : function(store,opts)
20375     {
20376          //Roo.log('onBeforeLoad');   
20377         if (!opts.add) {
20378             this.el.update("");
20379         }
20380         this.el.mask(this.mask ? this.mask : "Loading" ); 
20381     },
20382     onLoad : function ()
20383     {
20384         this.el.unmask();
20385     },
20386     
20387
20388     /**
20389      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20390      * @param {HTMLElement} node
20391      * @return {HTMLElement} The template node
20392      */
20393     findItemFromChild : function(node){
20394         var el = this.dataName  ?
20395             this.el.child('.roo-tpl-' + this.dataName,true) :
20396             this.el.dom; 
20397         
20398         if(!node || node.parentNode == el){
20399                     return node;
20400             }
20401             var p = node.parentNode;
20402             while(p && p != el){
20403             if(p.parentNode == el){
20404                 return p;
20405             }
20406             p = p.parentNode;
20407         }
20408             return null;
20409     },
20410
20411     /** @ignore */
20412     onClick : function(e){
20413         var item = this.findItemFromChild(e.getTarget());
20414         if(item){
20415             var index = this.indexOf(item);
20416             if(this.onItemClick(item, index, e) !== false){
20417                 this.fireEvent("click", this, index, item, e);
20418             }
20419         }else{
20420             this.clearSelections();
20421         }
20422     },
20423
20424     /** @ignore */
20425     onContextMenu : function(e){
20426         var item = this.findItemFromChild(e.getTarget());
20427         if(item){
20428             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20429         }
20430     },
20431
20432     /** @ignore */
20433     onDblClick : function(e){
20434         var item = this.findItemFromChild(e.getTarget());
20435         if(item){
20436             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20437         }
20438     },
20439
20440     onItemClick : function(item, index, e)
20441     {
20442         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20443             return false;
20444         }
20445         if (this.toggleSelect) {
20446             var m = this.isSelected(item) ? 'unselect' : 'select';
20447             //Roo.log(m);
20448             var _t = this;
20449             _t[m](item, true, false);
20450             return true;
20451         }
20452         if(this.multiSelect || this.singleSelect){
20453             if(this.multiSelect && e.shiftKey && this.lastSelection){
20454                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20455             }else{
20456                 this.select(item, this.multiSelect && e.ctrlKey);
20457                 this.lastSelection = item;
20458             }
20459             
20460             if(!this.tickable){
20461                 e.preventDefault();
20462             }
20463             
20464         }
20465         return true;
20466     },
20467
20468     /**
20469      * Get the number of selected nodes.
20470      * @return {Number}
20471      */
20472     getSelectionCount : function(){
20473         return this.selections.length;
20474     },
20475
20476     /**
20477      * Get the currently selected nodes.
20478      * @return {Array} An array of HTMLElements
20479      */
20480     getSelectedNodes : function(){
20481         return this.selections;
20482     },
20483
20484     /**
20485      * Get the indexes of the selected nodes.
20486      * @return {Array}
20487      */
20488     getSelectedIndexes : function(){
20489         var indexes = [], s = this.selections;
20490         for(var i = 0, len = s.length; i < len; i++){
20491             indexes.push(s[i].nodeIndex);
20492         }
20493         return indexes;
20494     },
20495
20496     /**
20497      * Clear all selections
20498      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20499      */
20500     clearSelections : function(suppressEvent){
20501         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20502             this.cmp.elements = this.selections;
20503             this.cmp.removeClass(this.selectedClass);
20504             this.selections = [];
20505             if(!suppressEvent){
20506                 this.fireEvent("selectionchange", this, this.selections);
20507             }
20508         }
20509     },
20510
20511     /**
20512      * Returns true if the passed node is selected
20513      * @param {HTMLElement/Number} node The node or node index
20514      * @return {Boolean}
20515      */
20516     isSelected : function(node){
20517         var s = this.selections;
20518         if(s.length < 1){
20519             return false;
20520         }
20521         node = this.getNode(node);
20522         return s.indexOf(node) !== -1;
20523     },
20524
20525     /**
20526      * Selects 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 to keep existing selections
20529      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20530      */
20531     select : function(nodeInfo, keepExisting, suppressEvent){
20532         if(nodeInfo instanceof Array){
20533             if(!keepExisting){
20534                 this.clearSelections(true);
20535             }
20536             for(var i = 0, len = nodeInfo.length; i < len; i++){
20537                 this.select(nodeInfo[i], true, true);
20538             }
20539             return;
20540         } 
20541         var node = this.getNode(nodeInfo);
20542         if(!node || this.isSelected(node)){
20543             return; // already selected.
20544         }
20545         if(!keepExisting){
20546             this.clearSelections(true);
20547         }
20548         
20549         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20550             Roo.fly(node).addClass(this.selectedClass);
20551             this.selections.push(node);
20552             if(!suppressEvent){
20553                 this.fireEvent("selectionchange", this, this.selections);
20554             }
20555         }
20556         
20557         
20558     },
20559       /**
20560      * Unselects nodes.
20561      * @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
20562      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20563      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20564      */
20565     unselect : function(nodeInfo, keepExisting, suppressEvent)
20566     {
20567         if(nodeInfo instanceof Array){
20568             Roo.each(this.selections, function(s) {
20569                 this.unselect(s, nodeInfo);
20570             }, this);
20571             return;
20572         }
20573         var node = this.getNode(nodeInfo);
20574         if(!node || !this.isSelected(node)){
20575             //Roo.log("not selected");
20576             return; // not selected.
20577         }
20578         // fireevent???
20579         var ns = [];
20580         Roo.each(this.selections, function(s) {
20581             if (s == node ) {
20582                 Roo.fly(node).removeClass(this.selectedClass);
20583
20584                 return;
20585             }
20586             ns.push(s);
20587         },this);
20588         
20589         this.selections= ns;
20590         this.fireEvent("selectionchange", this, this.selections);
20591     },
20592
20593     /**
20594      * Gets a template node.
20595      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20596      * @return {HTMLElement} The node or null if it wasn't found
20597      */
20598     getNode : function(nodeInfo){
20599         if(typeof nodeInfo == "string"){
20600             return document.getElementById(nodeInfo);
20601         }else if(typeof nodeInfo == "number"){
20602             return this.nodes[nodeInfo];
20603         }
20604         return nodeInfo;
20605     },
20606
20607     /**
20608      * Gets a range template nodes.
20609      * @param {Number} startIndex
20610      * @param {Number} endIndex
20611      * @return {Array} An array of nodes
20612      */
20613     getNodes : function(start, end){
20614         var ns = this.nodes;
20615         start = start || 0;
20616         end = typeof end == "undefined" ? ns.length - 1 : end;
20617         var nodes = [];
20618         if(start <= end){
20619             for(var i = start; i <= end; i++){
20620                 nodes.push(ns[i]);
20621             }
20622         } else{
20623             for(var i = start; i >= end; i--){
20624                 nodes.push(ns[i]);
20625             }
20626         }
20627         return nodes;
20628     },
20629
20630     /**
20631      * Finds the index of the passed node
20632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20633      * @return {Number} The index of the node or -1
20634      */
20635     indexOf : function(node){
20636         node = this.getNode(node);
20637         if(typeof node.nodeIndex == "number"){
20638             return node.nodeIndex;
20639         }
20640         var ns = this.nodes;
20641         for(var i = 0, len = ns.length; i < len; i++){
20642             if(ns[i] == node){
20643                 return i;
20644             }
20645         }
20646         return -1;
20647     }
20648 });
20649 /*
20650  * - LGPL
20651  *
20652  * based on jquery fullcalendar
20653  * 
20654  */
20655
20656 Roo.bootstrap = Roo.bootstrap || {};
20657 /**
20658  * @class Roo.bootstrap.Calendar
20659  * @extends Roo.bootstrap.Component
20660  * Bootstrap Calendar class
20661  * @cfg {Boolean} loadMask (true|false) default false
20662  * @cfg {Object} header generate the user specific header of the calendar, default false
20663
20664  * @constructor
20665  * Create a new Container
20666  * @param {Object} config The config object
20667  */
20668
20669
20670
20671 Roo.bootstrap.Calendar = function(config){
20672     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20673      this.addEvents({
20674         /**
20675              * @event select
20676              * Fires when a date is selected
20677              * @param {DatePicker} this
20678              * @param {Date} date The selected date
20679              */
20680         'select': true,
20681         /**
20682              * @event monthchange
20683              * Fires when the displayed month changes 
20684              * @param {DatePicker} this
20685              * @param {Date} date The selected month
20686              */
20687         'monthchange': true,
20688         /**
20689              * @event evententer
20690              * Fires when mouse over an event
20691              * @param {Calendar} this
20692              * @param {event} Event
20693              */
20694         'evententer': true,
20695         /**
20696              * @event eventleave
20697              * Fires when the mouse leaves an
20698              * @param {Calendar} this
20699              * @param {event}
20700              */
20701         'eventleave': true,
20702         /**
20703              * @event eventclick
20704              * Fires when the mouse click an
20705              * @param {Calendar} this
20706              * @param {event}
20707              */
20708         'eventclick': true
20709         
20710     });
20711
20712 };
20713
20714 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20715     
20716           /**
20717      * @cfg {Roo.data.Store} store
20718      * The data source for the calendar
20719      */
20720         store : false,
20721      /**
20722      * @cfg {Number} startDay
20723      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20724      */
20725     startDay : 0,
20726     
20727     loadMask : false,
20728     
20729     header : false,
20730       
20731     getAutoCreate : function(){
20732         
20733         
20734         var fc_button = function(name, corner, style, content ) {
20735             return Roo.apply({},{
20736                 tag : 'span',
20737                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20738                          (corner.length ?
20739                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20740                             ''
20741                         ),
20742                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20743                 unselectable: 'on'
20744             });
20745         };
20746         
20747         var header = {};
20748         
20749         if(!this.header){
20750             header = {
20751                 tag : 'table',
20752                 cls : 'fc-header',
20753                 style : 'width:100%',
20754                 cn : [
20755                     {
20756                         tag: 'tr',
20757                         cn : [
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-left',
20761                                 cn : [
20762                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20763                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20764                                     { tag: 'span', cls: 'fc-header-space' },
20765                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20766
20767
20768                                 ]
20769                             },
20770
20771                             {
20772                                 tag : 'td',
20773                                 cls : 'fc-header-center',
20774                                 cn : [
20775                                     {
20776                                         tag: 'span',
20777                                         cls: 'fc-header-title',
20778                                         cn : {
20779                                             tag: 'H2',
20780                                             html : 'month / year'
20781                                         }
20782                                     }
20783
20784                                 ]
20785                             },
20786                             {
20787                                 tag : 'td',
20788                                 cls : 'fc-header-right',
20789                                 cn : [
20790                               /*      fc_button('month', 'left', '', 'month' ),
20791                                     fc_button('week', '', '', 'week' ),
20792                                     fc_button('day', 'right', '', 'day' )
20793                                 */    
20794
20795                                 ]
20796                             }
20797
20798                         ]
20799                     }
20800                 ]
20801             };
20802         }
20803         
20804         header = this.header;
20805         
20806        
20807         var cal_heads = function() {
20808             var ret = [];
20809             // fixme - handle this.
20810             
20811             for (var i =0; i < Date.dayNames.length; i++) {
20812                 var d = Date.dayNames[i];
20813                 ret.push({
20814                     tag: 'th',
20815                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20816                     html : d.substring(0,3)
20817                 });
20818                 
20819             }
20820             ret[0].cls += ' fc-first';
20821             ret[6].cls += ' fc-last';
20822             return ret;
20823         };
20824         var cal_cell = function(n) {
20825             return  {
20826                 tag: 'td',
20827                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20828                 cn : [
20829                     {
20830                         cn : [
20831                             {
20832                                 cls: 'fc-day-number',
20833                                 html: 'D'
20834                             },
20835                             {
20836                                 cls: 'fc-day-content',
20837                              
20838                                 cn : [
20839                                      {
20840                                         style: 'position: relative;' // height: 17px;
20841                                     }
20842                                 ]
20843                             }
20844                             
20845                             
20846                         ]
20847                     }
20848                 ]
20849                 
20850             }
20851         };
20852         var cal_rows = function() {
20853             
20854             var ret = [];
20855             for (var r = 0; r < 6; r++) {
20856                 var row= {
20857                     tag : 'tr',
20858                     cls : 'fc-week',
20859                     cn : []
20860                 };
20861                 
20862                 for (var i =0; i < Date.dayNames.length; i++) {
20863                     var d = Date.dayNames[i];
20864                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20865
20866                 }
20867                 row.cn[0].cls+=' fc-first';
20868                 row.cn[0].cn[0].style = 'min-height:90px';
20869                 row.cn[6].cls+=' fc-last';
20870                 ret.push(row);
20871                 
20872             }
20873             ret[0].cls += ' fc-first';
20874             ret[4].cls += ' fc-prev-last';
20875             ret[5].cls += ' fc-last';
20876             return ret;
20877             
20878         };
20879         
20880         var cal_table = {
20881             tag: 'table',
20882             cls: 'fc-border-separate',
20883             style : 'width:100%',
20884             cellspacing  : 0,
20885             cn : [
20886                 { 
20887                     tag: 'thead',
20888                     cn : [
20889                         { 
20890                             tag: 'tr',
20891                             cls : 'fc-first fc-last',
20892                             cn : cal_heads()
20893                         }
20894                     ]
20895                 },
20896                 { 
20897                     tag: 'tbody',
20898                     cn : cal_rows()
20899                 }
20900                   
20901             ]
20902         };
20903          
20904          var cfg = {
20905             cls : 'fc fc-ltr',
20906             cn : [
20907                 header,
20908                 {
20909                     cls : 'fc-content',
20910                     style : "position: relative;",
20911                     cn : [
20912                         {
20913                             cls : 'fc-view fc-view-month fc-grid',
20914                             style : 'position: relative',
20915                             unselectable : 'on',
20916                             cn : [
20917                                 {
20918                                     cls : 'fc-event-container',
20919                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20920                                 },
20921                                 cal_table
20922                             ]
20923                         }
20924                     ]
20925     
20926                 }
20927            ] 
20928             
20929         };
20930         
20931          
20932         
20933         return cfg;
20934     },
20935     
20936     
20937     initEvents : function()
20938     {
20939         if(!this.store){
20940             throw "can not find store for calendar";
20941         }
20942         
20943         var mark = {
20944             tag: "div",
20945             cls:"x-dlg-mask",
20946             style: "text-align:center",
20947             cn: [
20948                 {
20949                     tag: "div",
20950                     style: "background-color:white;width:50%;margin:250 auto",
20951                     cn: [
20952                         {
20953                             tag: "img",
20954                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20955                         },
20956                         {
20957                             tag: "span",
20958                             html: "Loading"
20959                         }
20960                         
20961                     ]
20962                 }
20963             ]
20964         };
20965         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20966         
20967         var size = this.el.select('.fc-content', true).first().getSize();
20968         this.maskEl.setSize(size.width, size.height);
20969         this.maskEl.enableDisplayMode("block");
20970         if(!this.loadMask){
20971             this.maskEl.hide();
20972         }
20973         
20974         this.store = Roo.factory(this.store, Roo.data);
20975         this.store.on('load', this.onLoad, this);
20976         this.store.on('beforeload', this.onBeforeLoad, this);
20977         
20978         this.resize();
20979         
20980         this.cells = this.el.select('.fc-day',true);
20981         //Roo.log(this.cells);
20982         this.textNodes = this.el.query('.fc-day-number');
20983         this.cells.addClassOnOver('fc-state-hover');
20984         
20985         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20986         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20987         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20988         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20989         
20990         this.on('monthchange', this.onMonthChange, this);
20991         
20992         this.update(new Date().clearTime());
20993     },
20994     
20995     resize : function() {
20996         var sz  = this.el.getSize();
20997         
20998         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20999         this.el.select('.fc-day-content div',true).setHeight(34);
21000     },
21001     
21002     
21003     // private
21004     showPrevMonth : function(e){
21005         this.update(this.activeDate.add("mo", -1));
21006     },
21007     showToday : function(e){
21008         this.update(new Date().clearTime());
21009     },
21010     // private
21011     showNextMonth : function(e){
21012         this.update(this.activeDate.add("mo", 1));
21013     },
21014
21015     // private
21016     showPrevYear : function(){
21017         this.update(this.activeDate.add("y", -1));
21018     },
21019
21020     // private
21021     showNextYear : function(){
21022         this.update(this.activeDate.add("y", 1));
21023     },
21024
21025     
21026    // private
21027     update : function(date)
21028     {
21029         var vd = this.activeDate;
21030         this.activeDate = date;
21031 //        if(vd && this.el){
21032 //            var t = date.getTime();
21033 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21034 //                Roo.log('using add remove');
21035 //                
21036 //                this.fireEvent('monthchange', this, date);
21037 //                
21038 //                this.cells.removeClass("fc-state-highlight");
21039 //                this.cells.each(function(c){
21040 //                   if(c.dateValue == t){
21041 //                       c.addClass("fc-state-highlight");
21042 //                       setTimeout(function(){
21043 //                            try{c.dom.firstChild.focus();}catch(e){}
21044 //                       }, 50);
21045 //                       return false;
21046 //                   }
21047 //                   return true;
21048 //                });
21049 //                return;
21050 //            }
21051 //        }
21052         
21053         var days = date.getDaysInMonth();
21054         
21055         var firstOfMonth = date.getFirstDateOfMonth();
21056         var startingPos = firstOfMonth.getDay()-this.startDay;
21057         
21058         if(startingPos < this.startDay){
21059             startingPos += 7;
21060         }
21061         
21062         var pm = date.add(Date.MONTH, -1);
21063         var prevStart = pm.getDaysInMonth()-startingPos;
21064 //        
21065         this.cells = this.el.select('.fc-day',true);
21066         this.textNodes = this.el.query('.fc-day-number');
21067         this.cells.addClassOnOver('fc-state-hover');
21068         
21069         var cells = this.cells.elements;
21070         var textEls = this.textNodes;
21071         
21072         Roo.each(cells, function(cell){
21073             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21074         });
21075         
21076         days += startingPos;
21077
21078         // convert everything to numbers so it's fast
21079         var day = 86400000;
21080         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21081         //Roo.log(d);
21082         //Roo.log(pm);
21083         //Roo.log(prevStart);
21084         
21085         var today = new Date().clearTime().getTime();
21086         var sel = date.clearTime().getTime();
21087         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21088         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21089         var ddMatch = this.disabledDatesRE;
21090         var ddText = this.disabledDatesText;
21091         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21092         var ddaysText = this.disabledDaysText;
21093         var format = this.format;
21094         
21095         var setCellClass = function(cal, cell){
21096             cell.row = 0;
21097             cell.events = [];
21098             cell.more = [];
21099             //Roo.log('set Cell Class');
21100             cell.title = "";
21101             var t = d.getTime();
21102             
21103             //Roo.log(d);
21104             
21105             cell.dateValue = t;
21106             if(t == today){
21107                 cell.className += " fc-today";
21108                 cell.className += " fc-state-highlight";
21109                 cell.title = cal.todayText;
21110             }
21111             if(t == sel){
21112                 // disable highlight in other month..
21113                 //cell.className += " fc-state-highlight";
21114                 
21115             }
21116             // disabling
21117             if(t < min) {
21118                 cell.className = " fc-state-disabled";
21119                 cell.title = cal.minText;
21120                 return;
21121             }
21122             if(t > max) {
21123                 cell.className = " fc-state-disabled";
21124                 cell.title = cal.maxText;
21125                 return;
21126             }
21127             if(ddays){
21128                 if(ddays.indexOf(d.getDay()) != -1){
21129                     cell.title = ddaysText;
21130                     cell.className = " fc-state-disabled";
21131                 }
21132             }
21133             if(ddMatch && format){
21134                 var fvalue = d.dateFormat(format);
21135                 if(ddMatch.test(fvalue)){
21136                     cell.title = ddText.replace("%0", fvalue);
21137                     cell.className = " fc-state-disabled";
21138                 }
21139             }
21140             
21141             if (!cell.initialClassName) {
21142                 cell.initialClassName = cell.dom.className;
21143             }
21144             
21145             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21146         };
21147
21148         var i = 0;
21149         
21150         for(; i < startingPos; i++) {
21151             textEls[i].innerHTML = (++prevStart);
21152             d.setDate(d.getDate()+1);
21153             
21154             cells[i].className = "fc-past fc-other-month";
21155             setCellClass(this, cells[i]);
21156         }
21157         
21158         var intDay = 0;
21159         
21160         for(; i < days; i++){
21161             intDay = i - startingPos + 1;
21162             textEls[i].innerHTML = (intDay);
21163             d.setDate(d.getDate()+1);
21164             
21165             cells[i].className = ''; // "x-date-active";
21166             setCellClass(this, cells[i]);
21167         }
21168         var extraDays = 0;
21169         
21170         for(; i < 42; i++) {
21171             textEls[i].innerHTML = (++extraDays);
21172             d.setDate(d.getDate()+1);
21173             
21174             cells[i].className = "fc-future fc-other-month";
21175             setCellClass(this, cells[i]);
21176         }
21177         
21178         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21179         
21180         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21181         
21182         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21183         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21184         
21185         if(totalRows != 6){
21186             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21187             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21188         }
21189         
21190         this.fireEvent('monthchange', this, date);
21191         
21192         
21193         /*
21194         if(!this.internalRender){
21195             var main = this.el.dom.firstChild;
21196             var w = main.offsetWidth;
21197             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21198             Roo.fly(main).setWidth(w);
21199             this.internalRender = true;
21200             // opera does not respect the auto grow header center column
21201             // then, after it gets a width opera refuses to recalculate
21202             // without a second pass
21203             if(Roo.isOpera && !this.secondPass){
21204                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21205                 this.secondPass = true;
21206                 this.update.defer(10, this, [date]);
21207             }
21208         }
21209         */
21210         
21211     },
21212     
21213     findCell : function(dt) {
21214         dt = dt.clearTime().getTime();
21215         var ret = false;
21216         this.cells.each(function(c){
21217             //Roo.log("check " +c.dateValue + '?=' + dt);
21218             if(c.dateValue == dt){
21219                 ret = c;
21220                 return false;
21221             }
21222             return true;
21223         });
21224         
21225         return ret;
21226     },
21227     
21228     findCells : function(ev) {
21229         var s = ev.start.clone().clearTime().getTime();
21230        // Roo.log(s);
21231         var e= ev.end.clone().clearTime().getTime();
21232        // Roo.log(e);
21233         var ret = [];
21234         this.cells.each(function(c){
21235              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21236             
21237             if(c.dateValue > e){
21238                 return ;
21239             }
21240             if(c.dateValue < s){
21241                 return ;
21242             }
21243             ret.push(c);
21244         });
21245         
21246         return ret;    
21247     },
21248     
21249 //    findBestRow: function(cells)
21250 //    {
21251 //        var ret = 0;
21252 //        
21253 //        for (var i =0 ; i < cells.length;i++) {
21254 //            ret  = Math.max(cells[i].rows || 0,ret);
21255 //        }
21256 //        return ret;
21257 //        
21258 //    },
21259     
21260     
21261     addItem : function(ev)
21262     {
21263         // look for vertical location slot in
21264         var cells = this.findCells(ev);
21265         
21266 //        ev.row = this.findBestRow(cells);
21267         
21268         // work out the location.
21269         
21270         var crow = false;
21271         var rows = [];
21272         for(var i =0; i < cells.length; i++) {
21273             
21274             cells[i].row = cells[0].row;
21275             
21276             if(i == 0){
21277                 cells[i].row = cells[i].row + 1;
21278             }
21279             
21280             if (!crow) {
21281                 crow = {
21282                     start : cells[i],
21283                     end :  cells[i]
21284                 };
21285                 continue;
21286             }
21287             if (crow.start.getY() == cells[i].getY()) {
21288                 // on same row.
21289                 crow.end = cells[i];
21290                 continue;
21291             }
21292             // different row.
21293             rows.push(crow);
21294             crow = {
21295                 start: cells[i],
21296                 end : cells[i]
21297             };
21298             
21299         }
21300         
21301         rows.push(crow);
21302         ev.els = [];
21303         ev.rows = rows;
21304         ev.cells = cells;
21305         
21306         cells[0].events.push(ev);
21307         
21308         this.calevents.push(ev);
21309     },
21310     
21311     clearEvents: function() {
21312         
21313         if(!this.calevents){
21314             return;
21315         }
21316         
21317         Roo.each(this.cells.elements, function(c){
21318             c.row = 0;
21319             c.events = [];
21320             c.more = [];
21321         });
21322         
21323         Roo.each(this.calevents, function(e) {
21324             Roo.each(e.els, function(el) {
21325                 el.un('mouseenter' ,this.onEventEnter, this);
21326                 el.un('mouseleave' ,this.onEventLeave, this);
21327                 el.remove();
21328             },this);
21329         },this);
21330         
21331         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21332             e.remove();
21333         });
21334         
21335     },
21336     
21337     renderEvents: function()
21338     {   
21339         var _this = this;
21340         
21341         this.cells.each(function(c) {
21342             
21343             if(c.row < 5){
21344                 return;
21345             }
21346             
21347             var ev = c.events;
21348             
21349             var r = 4;
21350             if(c.row != c.events.length){
21351                 r = 4 - (4 - (c.row - c.events.length));
21352             }
21353             
21354             c.events = ev.slice(0, r);
21355             c.more = ev.slice(r);
21356             
21357             if(c.more.length && c.more.length == 1){
21358                 c.events.push(c.more.pop());
21359             }
21360             
21361             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21362             
21363         });
21364             
21365         this.cells.each(function(c) {
21366             
21367             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21368             
21369             
21370             for (var e = 0; e < c.events.length; e++){
21371                 var ev = c.events[e];
21372                 var rows = ev.rows;
21373                 
21374                 for(var i = 0; i < rows.length; i++) {
21375                 
21376                     // how many rows should it span..
21377
21378                     var  cfg = {
21379                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21380                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21381
21382                         unselectable : "on",
21383                         cn : [
21384                             {
21385                                 cls: 'fc-event-inner',
21386                                 cn : [
21387     //                                {
21388     //                                  tag:'span',
21389     //                                  cls: 'fc-event-time',
21390     //                                  html : cells.length > 1 ? '' : ev.time
21391     //                                },
21392                                     {
21393                                       tag:'span',
21394                                       cls: 'fc-event-title',
21395                                       html : String.format('{0}', ev.title)
21396                                     }
21397
21398
21399                                 ]
21400                             },
21401                             {
21402                                 cls: 'ui-resizable-handle ui-resizable-e',
21403                                 html : '&nbsp;&nbsp;&nbsp'
21404                             }
21405
21406                         ]
21407                     };
21408
21409                     if (i == 0) {
21410                         cfg.cls += ' fc-event-start';
21411                     }
21412                     if ((i+1) == rows.length) {
21413                         cfg.cls += ' fc-event-end';
21414                     }
21415
21416                     var ctr = _this.el.select('.fc-event-container',true).first();
21417                     var cg = ctr.createChild(cfg);
21418
21419                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21420                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21421
21422                     var r = (c.more.length) ? 1 : 0;
21423                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21424                     cg.setWidth(ebox.right - sbox.x -2);
21425
21426                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21427                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21428                     cg.on('click', _this.onEventClick, _this, ev);
21429
21430                     ev.els.push(cg);
21431                     
21432                 }
21433                 
21434             }
21435             
21436             
21437             if(c.more.length){
21438                 var  cfg = {
21439                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21440                     style : 'position: absolute',
21441                     unselectable : "on",
21442                     cn : [
21443                         {
21444                             cls: 'fc-event-inner',
21445                             cn : [
21446                                 {
21447                                   tag:'span',
21448                                   cls: 'fc-event-title',
21449                                   html : 'More'
21450                                 }
21451
21452
21453                             ]
21454                         },
21455                         {
21456                             cls: 'ui-resizable-handle ui-resizable-e',
21457                             html : '&nbsp;&nbsp;&nbsp'
21458                         }
21459
21460                     ]
21461                 };
21462
21463                 var ctr = _this.el.select('.fc-event-container',true).first();
21464                 var cg = ctr.createChild(cfg);
21465
21466                 var sbox = c.select('.fc-day-content',true).first().getBox();
21467                 var ebox = c.select('.fc-day-content',true).first().getBox();
21468                 //Roo.log(cg);
21469                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21470                 cg.setWidth(ebox.right - sbox.x -2);
21471
21472                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21473                 
21474             }
21475             
21476         });
21477         
21478         
21479         
21480     },
21481     
21482     onEventEnter: function (e, el,event,d) {
21483         this.fireEvent('evententer', this, el, event);
21484     },
21485     
21486     onEventLeave: function (e, el,event,d) {
21487         this.fireEvent('eventleave', this, el, event);
21488     },
21489     
21490     onEventClick: function (e, el,event,d) {
21491         this.fireEvent('eventclick', this, el, event);
21492     },
21493     
21494     onMonthChange: function () {
21495         this.store.load();
21496     },
21497     
21498     onMoreEventClick: function(e, el, more)
21499     {
21500         var _this = this;
21501         
21502         this.calpopover.placement = 'right';
21503         this.calpopover.setTitle('More');
21504         
21505         this.calpopover.setContent('');
21506         
21507         var ctr = this.calpopover.el.select('.popover-content', true).first();
21508         
21509         Roo.each(more, function(m){
21510             var cfg = {
21511                 cls : 'fc-event-hori fc-event-draggable',
21512                 html : m.title
21513             };
21514             var cg = ctr.createChild(cfg);
21515             
21516             cg.on('click', _this.onEventClick, _this, m);
21517         });
21518         
21519         this.calpopover.show(el);
21520         
21521         
21522     },
21523     
21524     onLoad: function () 
21525     {   
21526         this.calevents = [];
21527         var cal = this;
21528         
21529         if(this.store.getCount() > 0){
21530             this.store.data.each(function(d){
21531                cal.addItem({
21532                     id : d.data.id,
21533                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21534                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21535                     time : d.data.start_time,
21536                     title : d.data.title,
21537                     description : d.data.description,
21538                     venue : d.data.venue
21539                 });
21540             });
21541         }
21542         
21543         this.renderEvents();
21544         
21545         if(this.calevents.length && this.loadMask){
21546             this.maskEl.hide();
21547         }
21548     },
21549     
21550     onBeforeLoad: function()
21551     {
21552         this.clearEvents();
21553         if(this.loadMask){
21554             this.maskEl.show();
21555         }
21556     }
21557 });
21558
21559  
21560  /*
21561  * - LGPL
21562  *
21563  * element
21564  * 
21565  */
21566
21567 /**
21568  * @class Roo.bootstrap.Popover
21569  * @extends Roo.bootstrap.Component
21570  * @parent none builder
21571  * @children Roo.bootstrap.Component
21572  * Bootstrap Popover class
21573  * @cfg {String} html contents of the popover   (or false to use children..)
21574  * @cfg {String} title of popover (or false to hide)
21575  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21576  * @cfg {String} trigger click || hover (or false to trigger manually)
21577  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21578  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21579  *      - if false and it has a 'parent' then it will be automatically added to that element
21580  *      - if string - Roo.get  will be called 
21581  * @cfg {Number} delay - delay before showing
21582  
21583  * @constructor
21584  * Create a new Popover
21585  * @param {Object} config The config object
21586  */
21587
21588 Roo.bootstrap.Popover = function(config){
21589     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21590     
21591     this.addEvents({
21592         // raw events
21593          /**
21594          * @event show
21595          * After the popover show
21596          * 
21597          * @param {Roo.bootstrap.Popover} this
21598          */
21599         "show" : true,
21600         /**
21601          * @event hide
21602          * After the popover hide
21603          * 
21604          * @param {Roo.bootstrap.Popover} this
21605          */
21606         "hide" : true
21607     });
21608 };
21609
21610 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21611     
21612     title: false,
21613     html: false,
21614     
21615     placement : 'right',
21616     trigger : 'hover', // hover
21617     modal : false,
21618     delay : 0,
21619     
21620     over: false,
21621     
21622     can_build_overlaid : false,
21623     
21624     maskEl : false, // the mask element
21625     headerEl : false,
21626     contentEl : false,
21627     alignEl : false, // when show is called with an element - this get's stored.
21628     
21629     getChildContainer : function()
21630     {
21631         return this.contentEl;
21632         
21633     },
21634     getPopoverHeader : function()
21635     {
21636         this.title = true; // flag not to hide it..
21637         this.headerEl.addClass('p-0');
21638         return this.headerEl
21639     },
21640     
21641     
21642     getAutoCreate : function(){
21643          
21644         var cfg = {
21645            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21646            style: 'display:block',
21647            cn : [
21648                 {
21649                     cls : 'arrow'
21650                 },
21651                 {
21652                     cls : 'popover-inner ',
21653                     cn : [
21654                         {
21655                             tag: 'h3',
21656                             cls: 'popover-title popover-header',
21657                             html : this.title === false ? '' : this.title
21658                         },
21659                         {
21660                             cls : 'popover-content popover-body '  + (this.cls || ''),
21661                             html : this.html || ''
21662                         }
21663                     ]
21664                     
21665                 }
21666            ]
21667         };
21668         
21669         return cfg;
21670     },
21671     /**
21672      * @param {string} the title
21673      */
21674     setTitle: function(str)
21675     {
21676         this.title = str;
21677         if (this.el) {
21678             this.headerEl.dom.innerHTML = str;
21679         }
21680         
21681     },
21682     /**
21683      * @param {string} the body content
21684      */
21685     setContent: function(str)
21686     {
21687         this.html = str;
21688         if (this.contentEl) {
21689             this.contentEl.dom.innerHTML = str;
21690         }
21691         
21692     },
21693     // as it get's added to the bottom of the page.
21694     onRender : function(ct, position)
21695     {
21696         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21697         
21698         
21699         
21700         if(!this.el){
21701             var cfg = Roo.apply({},  this.getAutoCreate());
21702             cfg.id = Roo.id();
21703             
21704             if (this.cls) {
21705                 cfg.cls += ' ' + this.cls;
21706             }
21707             if (this.style) {
21708                 cfg.style = this.style;
21709             }
21710             //Roo.log("adding to ");
21711             this.el = Roo.get(document.body).createChild(cfg, position);
21712 //            Roo.log(this.el);
21713         }
21714         
21715         this.contentEl = this.el.select('.popover-content',true).first();
21716         this.headerEl =  this.el.select('.popover-title',true).first();
21717         
21718         var nitems = [];
21719         if(typeof(this.items) != 'undefined'){
21720             var items = this.items;
21721             delete this.items;
21722
21723             for(var i =0;i < items.length;i++) {
21724                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21725             }
21726         }
21727
21728         this.items = nitems;
21729         
21730         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21731         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21732         
21733         
21734         
21735         this.initEvents();
21736     },
21737     
21738     resizeMask : function()
21739     {
21740         this.maskEl.setSize(
21741             Roo.lib.Dom.getViewWidth(true),
21742             Roo.lib.Dom.getViewHeight(true)
21743         );
21744     },
21745     
21746     initEvents : function()
21747     {
21748         
21749         if (!this.modal) { 
21750             Roo.bootstrap.Popover.register(this);
21751         }
21752          
21753         this.arrowEl = this.el.select('.arrow',true).first();
21754         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21755         this.el.enableDisplayMode('block');
21756         this.el.hide();
21757  
21758         
21759         if (this.over === false && !this.parent()) {
21760             return; 
21761         }
21762         if (this.triggers === false) {
21763             return;
21764         }
21765          
21766         // support parent
21767         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21768         var triggers = this.trigger ? this.trigger.split(' ') : [];
21769         Roo.each(triggers, function(trigger) {
21770         
21771             if (trigger == 'click') {
21772                 on_el.on('click', this.toggle, this);
21773             } else if (trigger != 'manual') {
21774                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21775                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21776       
21777                 on_el.on(eventIn  ,this.enter, this);
21778                 on_el.on(eventOut, this.leave, this);
21779             }
21780         }, this);
21781     },
21782     
21783     
21784     // private
21785     timeout : null,
21786     hoverState : null,
21787     
21788     toggle : function () {
21789         this.hoverState == 'in' ? this.leave() : this.enter();
21790     },
21791     
21792     enter : function () {
21793         
21794         clearTimeout(this.timeout);
21795     
21796         this.hoverState = 'in';
21797     
21798         if (!this.delay || !this.delay.show) {
21799             this.show();
21800             return;
21801         }
21802         var _t = this;
21803         this.timeout = setTimeout(function () {
21804             if (_t.hoverState == 'in') {
21805                 _t.show();
21806             }
21807         }, this.delay.show)
21808     },
21809     
21810     leave : function() {
21811         clearTimeout(this.timeout);
21812     
21813         this.hoverState = 'out';
21814     
21815         if (!this.delay || !this.delay.hide) {
21816             this.hide();
21817             return;
21818         }
21819         var _t = this;
21820         this.timeout = setTimeout(function () {
21821             if (_t.hoverState == 'out') {
21822                 _t.hide();
21823             }
21824         }, this.delay.hide)
21825     },
21826     
21827     /**
21828      * update the position of the dialog
21829      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21830      * 
21831      *
21832      */
21833     
21834     doAlign : function()
21835     {
21836         
21837         if (this.alignEl) {
21838             this.updatePosition(this.placement, true);
21839              
21840         } else {
21841             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21842             var es = this.el.getSize();
21843             var x = Roo.lib.Dom.getViewWidth()/2;
21844             var y = Roo.lib.Dom.getViewHeight()/2;
21845             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21846             
21847         }
21848
21849          
21850          
21851         
21852         
21853     },
21854     
21855     /**
21856      * Show the popover
21857      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21858      * @param {string} (left|right|top|bottom) position
21859      */
21860     show : function (on_el, placement)
21861     {
21862         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21863         on_el = on_el || false; // default to false
21864          
21865         if (!on_el) {
21866             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21867                 on_el = this.parent().el;
21868             } else if (this.over) {
21869                 on_el = Roo.get(this.over);
21870             }
21871             
21872         }
21873         
21874         this.alignEl = Roo.get( on_el );
21875
21876         if (!this.el) {
21877             this.render(document.body);
21878         }
21879         
21880         
21881          
21882         
21883         if (this.title === false) {
21884             this.headerEl.hide();
21885         }
21886         
21887        
21888         this.el.show();
21889         this.el.dom.style.display = 'block';
21890          
21891         this.doAlign();
21892         
21893         //var arrow = this.el.select('.arrow',true).first();
21894         //arrow.set(align[2], 
21895         
21896         this.el.addClass('in');
21897         
21898          
21899         
21900         this.hoverState = 'in';
21901         
21902         if (this.modal) {
21903             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21904             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21905             this.maskEl.dom.style.display = 'block';
21906             this.maskEl.addClass('show');
21907         }
21908         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21909  
21910         this.fireEvent('show', this);
21911         
21912     },
21913     /**
21914      * fire this manually after loading a grid in the table for example
21915      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21916      * @param {Boolean} try and move it if we cant get right position.
21917      */
21918     updatePosition : function(placement, try_move)
21919     {
21920         // allow for calling with no parameters
21921         placement = placement   ? placement :  this.placement;
21922         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21923         
21924         this.el.removeClass([
21925             'fade','top','bottom', 'left', 'right','in',
21926             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21927         ]);
21928         this.el.addClass(placement + ' bs-popover-' + placement);
21929         
21930         if (!this.alignEl ) {
21931             return false;
21932         }
21933         
21934         switch (placement) {
21935             case 'right':
21936                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21937                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21938                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21939                     //normal display... or moved up/down.
21940                     this.el.setXY(offset);
21941                     var xy = this.alignEl.getAnchorXY('tr', false);
21942                     xy[0]+=2;xy[1]+=5;
21943                     this.arrowEl.setXY(xy);
21944                     return true;
21945                 }
21946                 // continue through...
21947                 return this.updatePosition('left', false);
21948                 
21949             
21950             case 'left':
21951                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21953                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('tl', false);
21957                     xy[0]-=10;xy[1]+=5; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // call self...
21962                 return this.updatePosition('right', false);
21963             
21964             case 'top':
21965                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21966                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21967                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21968                     //normal display... or moved up/down.
21969                     this.el.setXY(offset);
21970                     var xy = this.alignEl.getAnchorXY('t', false);
21971                     xy[1]-=10; // << fix me
21972                     this.arrowEl.setXY(xy);
21973                     return true;
21974                 }
21975                 // fall through
21976                return this.updatePosition('bottom', false);
21977             
21978             case 'bottom':
21979                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21980                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21981                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21982                     //normal display... or moved up/down.
21983                     this.el.setXY(offset);
21984                     var xy = this.alignEl.getAnchorXY('b', false);
21985                      xy[1]+=2; // << fix me
21986                     this.arrowEl.setXY(xy);
21987                     return true;
21988                 }
21989                 // fall through
21990                 return this.updatePosition('top', false);
21991                 
21992             
21993         }
21994         
21995         
21996         return false;
21997     },
21998     
21999     hide : function()
22000     {
22001         this.el.setXY([0,0]);
22002         this.el.removeClass('in');
22003         this.el.hide();
22004         this.hoverState = null;
22005         this.maskEl.hide(); // always..
22006         this.fireEvent('hide', this);
22007     }
22008     
22009 });
22010
22011
22012 Roo.apply(Roo.bootstrap.Popover, {
22013
22014     alignment : {
22015         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22016         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22017         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22018         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22019     },
22020     
22021     zIndex : 20001,
22022
22023     clickHander : false,
22024     
22025     
22026
22027     onMouseDown : function(e)
22028     {
22029         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22030             /// what is nothing is showing..
22031             this.hideAll();
22032         }
22033          
22034     },
22035     
22036     
22037     popups : [],
22038     
22039     register : function(popup)
22040     {
22041         if (!Roo.bootstrap.Popover.clickHandler) {
22042             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22043         }
22044         // hide other popups.
22045         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22046         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22047         this.hideAll(); //<< why?
22048         //this.popups.push(popup);
22049     },
22050     hideAll : function()
22051     {
22052         this.popups.forEach(function(p) {
22053             p.hide();
22054         });
22055     },
22056     onShow : function() {
22057         Roo.bootstrap.Popover.popups.push(this);
22058     },
22059     onHide : function() {
22060         Roo.bootstrap.Popover.popups.remove(this);
22061     } 
22062
22063 });
22064 /**
22065  * @class Roo.bootstrap.PopoverNav
22066  * @extends Roo.bootstrap.nav.Simplebar
22067  * @parent Roo.bootstrap.Popover
22068  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22069  * @licence LGPL
22070  * Bootstrap Popover header navigation class
22071  * FIXME? should this go under nav?
22072  *
22073  * 
22074  * @constructor
22075  * Create a new Popover Header Navigation 
22076  * @param {Object} config The config object
22077  */
22078
22079 Roo.bootstrap.PopoverNav = function(config){
22080     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22081 };
22082
22083 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22084     
22085     
22086     container_method : 'getPopoverHeader' 
22087     
22088      
22089     
22090     
22091    
22092 });
22093
22094  
22095
22096  /*
22097  * - LGPL
22098  *
22099  * Progress
22100  * 
22101  */
22102
22103 /**
22104  * @class Roo.bootstrap.Progress
22105  * @extends Roo.bootstrap.Component
22106  * @children Roo.bootstrap.ProgressBar
22107  * Bootstrap Progress class
22108  * @cfg {Boolean} striped striped of the progress bar
22109  * @cfg {Boolean} active animated of the progress bar
22110  * 
22111  * 
22112  * @constructor
22113  * Create a new Progress
22114  * @param {Object} config The config object
22115  */
22116
22117 Roo.bootstrap.Progress = function(config){
22118     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22119 };
22120
22121 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22122     
22123     striped : false,
22124     active: false,
22125     
22126     getAutoCreate : function(){
22127         var cfg = {
22128             tag: 'div',
22129             cls: 'progress'
22130         };
22131         
22132         
22133         if(this.striped){
22134             cfg.cls += ' progress-striped';
22135         }
22136       
22137         if(this.active){
22138             cfg.cls += ' active';
22139         }
22140         
22141         
22142         return cfg;
22143     }
22144    
22145 });
22146
22147  
22148
22149  /*
22150  * - LGPL
22151  *
22152  * ProgressBar
22153  * 
22154  */
22155
22156 /**
22157  * @class Roo.bootstrap.ProgressBar
22158  * @extends Roo.bootstrap.Component
22159  * Bootstrap ProgressBar class
22160  * @cfg {Number} aria_valuenow aria-value now
22161  * @cfg {Number} aria_valuemin aria-value min
22162  * @cfg {Number} aria_valuemax aria-value max
22163  * @cfg {String} label label for the progress bar
22164  * @cfg {String} panel (success | info | warning | danger )
22165  * @cfg {String} role role of the progress bar
22166  * @cfg {String} sr_only text
22167  * 
22168  * 
22169  * @constructor
22170  * Create a new ProgressBar
22171  * @param {Object} config The config object
22172  */
22173
22174 Roo.bootstrap.ProgressBar = function(config){
22175     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22176 };
22177
22178 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22179     
22180     aria_valuenow : 0,
22181     aria_valuemin : 0,
22182     aria_valuemax : 100,
22183     label : false,
22184     panel : false,
22185     role : false,
22186     sr_only: false,
22187     
22188     getAutoCreate : function()
22189     {
22190         
22191         var cfg = {
22192             tag: 'div',
22193             cls: 'progress-bar',
22194             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22195         };
22196         
22197         if(this.sr_only){
22198             cfg.cn = {
22199                 tag: 'span',
22200                 cls: 'sr-only',
22201                 html: this.sr_only
22202             }
22203         }
22204         
22205         if(this.role){
22206             cfg.role = this.role;
22207         }
22208         
22209         if(this.aria_valuenow){
22210             cfg['aria-valuenow'] = this.aria_valuenow;
22211         }
22212         
22213         if(this.aria_valuemin){
22214             cfg['aria-valuemin'] = this.aria_valuemin;
22215         }
22216         
22217         if(this.aria_valuemax){
22218             cfg['aria-valuemax'] = this.aria_valuemax;
22219         }
22220         
22221         if(this.label && !this.sr_only){
22222             cfg.html = this.label;
22223         }
22224         
22225         if(this.panel){
22226             cfg.cls += ' progress-bar-' + this.panel;
22227         }
22228         
22229         return cfg;
22230     },
22231     
22232     update : function(aria_valuenow)
22233     {
22234         this.aria_valuenow = aria_valuenow;
22235         
22236         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22237     }
22238    
22239 });
22240
22241  
22242
22243  /**
22244  * @class Roo.bootstrap.TabGroup
22245  * @extends Roo.bootstrap.Column
22246  * @children Roo.bootstrap.TabPanel
22247  * Bootstrap Column class
22248  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22249  * @cfg {Boolean} carousel true to make the group behave like a carousel
22250  * @cfg {Boolean} bullets show bullets for the panels
22251  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22252  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22253  * @cfg {Boolean} showarrow (true|false) show arrow default true
22254  * 
22255  * @constructor
22256  * Create a new TabGroup
22257  * @param {Object} config The config object
22258  */
22259
22260 Roo.bootstrap.TabGroup = function(config){
22261     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22262     if (!this.navId) {
22263         this.navId = Roo.id();
22264     }
22265     this.tabs = [];
22266     Roo.bootstrap.TabGroup.register(this);
22267     
22268 };
22269
22270 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22271     
22272     carousel : false,
22273     transition : false,
22274     bullets : 0,
22275     timer : 0,
22276     autoslide : false,
22277     slideFn : false,
22278     slideOnTouch : false,
22279     showarrow : true,
22280     
22281     getAutoCreate : function()
22282     {
22283         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22284         
22285         cfg.cls += ' tab-content';
22286         
22287         if (this.carousel) {
22288             cfg.cls += ' carousel slide';
22289             
22290             cfg.cn = [{
22291                cls : 'carousel-inner',
22292                cn : []
22293             }];
22294         
22295             if(this.bullets  && !Roo.isTouch){
22296                 
22297                 var bullets = {
22298                     cls : 'carousel-bullets',
22299                     cn : []
22300                 };
22301                
22302                 if(this.bullets_cls){
22303                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22304                 }
22305                 
22306                 bullets.cn.push({
22307                     cls : 'clear'
22308                 });
22309                 
22310                 cfg.cn[0].cn.push(bullets);
22311             }
22312             
22313             if(this.showarrow){
22314                 cfg.cn[0].cn.push({
22315                     tag : 'div',
22316                     class : 'carousel-arrow',
22317                     cn : [
22318                         {
22319                             tag : 'div',
22320                             class : 'carousel-prev',
22321                             cn : [
22322                                 {
22323                                     tag : 'i',
22324                                     class : 'fa fa-chevron-left'
22325                                 }
22326                             ]
22327                         },
22328                         {
22329                             tag : 'div',
22330                             class : 'carousel-next',
22331                             cn : [
22332                                 {
22333                                     tag : 'i',
22334                                     class : 'fa fa-chevron-right'
22335                                 }
22336                             ]
22337                         }
22338                     ]
22339                 });
22340             }
22341             
22342         }
22343         
22344         return cfg;
22345     },
22346     
22347     initEvents:  function()
22348     {
22349 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22350 //            this.el.on("touchstart", this.onTouchStart, this);
22351 //        }
22352         
22353         if(this.autoslide){
22354             var _this = this;
22355             
22356             this.slideFn = window.setInterval(function() {
22357                 _this.showPanelNext();
22358             }, this.timer);
22359         }
22360         
22361         if(this.showarrow){
22362             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22363             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22364         }
22365         
22366         
22367     },
22368     
22369 //    onTouchStart : function(e, el, o)
22370 //    {
22371 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22372 //            return;
22373 //        }
22374 //        
22375 //        this.showPanelNext();
22376 //    },
22377     
22378     
22379     getChildContainer : function()
22380     {
22381         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22382     },
22383     
22384     /**
22385     * register a Navigation item
22386     * @param {Roo.bootstrap.nav.Item} the navitem to add
22387     */
22388     register : function(item)
22389     {
22390         this.tabs.push( item);
22391         item.navId = this.navId; // not really needed..
22392         this.addBullet();
22393     
22394     },
22395     
22396     getActivePanel : function()
22397     {
22398         var r = false;
22399         Roo.each(this.tabs, function(t) {
22400             if (t.active) {
22401                 r = t;
22402                 return false;
22403             }
22404             return null;
22405         });
22406         return r;
22407         
22408     },
22409     getPanelByName : function(n)
22410     {
22411         var r = false;
22412         Roo.each(this.tabs, function(t) {
22413             if (t.tabId == n) {
22414                 r = t;
22415                 return false;
22416             }
22417             return null;
22418         });
22419         return r;
22420     },
22421     indexOfPanel : function(p)
22422     {
22423         var r = false;
22424         Roo.each(this.tabs, function(t,i) {
22425             if (t.tabId == p.tabId) {
22426                 r = i;
22427                 return false;
22428             }
22429             return null;
22430         });
22431         return r;
22432     },
22433     /**
22434      * show a specific panel
22435      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22436      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22437      */
22438     showPanel : function (pan)
22439     {
22440         if(this.transition || typeof(pan) == 'undefined'){
22441             Roo.log("waiting for the transitionend");
22442             return false;
22443         }
22444         
22445         if (typeof(pan) == 'number') {
22446             pan = this.tabs[pan];
22447         }
22448         
22449         if (typeof(pan) == 'string') {
22450             pan = this.getPanelByName(pan);
22451         }
22452         
22453         var cur = this.getActivePanel();
22454         
22455         if(!pan || !cur){
22456             Roo.log('pan or acitve pan is undefined');
22457             return false;
22458         }
22459         
22460         if (pan.tabId == this.getActivePanel().tabId) {
22461             return true;
22462         }
22463         
22464         if (false === cur.fireEvent('beforedeactivate')) {
22465             return false;
22466         }
22467         
22468         if(this.bullets > 0 && !Roo.isTouch){
22469             this.setActiveBullet(this.indexOfPanel(pan));
22470         }
22471         
22472         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22473             
22474             //class="carousel-item carousel-item-next carousel-item-left"
22475             
22476             this.transition = true;
22477             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22478             var lr = dir == 'next' ? 'left' : 'right';
22479             pan.el.addClass(dir); // or prev
22480             pan.el.addClass('carousel-item-' + dir); // or prev
22481             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22482             cur.el.addClass(lr); // or right
22483             pan.el.addClass(lr);
22484             cur.el.addClass('carousel-item-' +lr); // or right
22485             pan.el.addClass('carousel-item-' +lr);
22486             
22487             
22488             var _this = this;
22489             cur.el.on('transitionend', function() {
22490                 Roo.log("trans end?");
22491                 
22492                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22493                 pan.setActive(true);
22494                 
22495                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22496                 cur.setActive(false);
22497                 
22498                 _this.transition = false;
22499                 
22500             }, this, { single:  true } );
22501             
22502             return true;
22503         }
22504         
22505         cur.setActive(false);
22506         pan.setActive(true);
22507         
22508         return true;
22509         
22510     },
22511     showPanelNext : function()
22512     {
22513         var i = this.indexOfPanel(this.getActivePanel());
22514         
22515         if (i >= this.tabs.length - 1 && !this.autoslide) {
22516             return;
22517         }
22518         
22519         if (i >= this.tabs.length - 1 && this.autoslide) {
22520             i = -1;
22521         }
22522         
22523         this.showPanel(this.tabs[i+1]);
22524     },
22525     
22526     showPanelPrev : function()
22527     {
22528         var i = this.indexOfPanel(this.getActivePanel());
22529         
22530         if (i  < 1 && !this.autoslide) {
22531             return;
22532         }
22533         
22534         if (i < 1 && this.autoslide) {
22535             i = this.tabs.length;
22536         }
22537         
22538         this.showPanel(this.tabs[i-1]);
22539     },
22540     
22541     
22542     addBullet: function()
22543     {
22544         if(!this.bullets || Roo.isTouch){
22545             return;
22546         }
22547         var ctr = this.el.select('.carousel-bullets',true).first();
22548         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22549         var bullet = ctr.createChild({
22550             cls : 'bullet bullet-' + i
22551         },ctr.dom.lastChild);
22552         
22553         
22554         var _this = this;
22555         
22556         bullet.on('click', (function(e, el, o, ii, t){
22557
22558             e.preventDefault();
22559
22560             this.showPanel(ii);
22561
22562             if(this.autoslide && this.slideFn){
22563                 clearInterval(this.slideFn);
22564                 this.slideFn = window.setInterval(function() {
22565                     _this.showPanelNext();
22566                 }, this.timer);
22567             }
22568
22569         }).createDelegate(this, [i, bullet], true));
22570                 
22571         
22572     },
22573      
22574     setActiveBullet : function(i)
22575     {
22576         if(Roo.isTouch){
22577             return;
22578         }
22579         
22580         Roo.each(this.el.select('.bullet', true).elements, function(el){
22581             el.removeClass('selected');
22582         });
22583
22584         var bullet = this.el.select('.bullet-' + i, true).first();
22585         
22586         if(!bullet){
22587             return;
22588         }
22589         
22590         bullet.addClass('selected');
22591     }
22592     
22593     
22594   
22595 });
22596
22597  
22598
22599  
22600  
22601 Roo.apply(Roo.bootstrap.TabGroup, {
22602     
22603     groups: {},
22604      /**
22605     * register a Navigation Group
22606     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22607     */
22608     register : function(navgrp)
22609     {
22610         this.groups[navgrp.navId] = navgrp;
22611         
22612     },
22613     /**
22614     * fetch a Navigation Group based on the navigation ID
22615     * if one does not exist , it will get created.
22616     * @param {string} the navgroup to add
22617     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22618     */
22619     get: function(navId) {
22620         if (typeof(this.groups[navId]) == 'undefined') {
22621             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22622         }
22623         return this.groups[navId] ;
22624     }
22625     
22626     
22627     
22628 });
22629
22630  /*
22631  * - LGPL
22632  *
22633  * TabPanel
22634  * 
22635  */
22636
22637 /**
22638  * @class Roo.bootstrap.TabPanel
22639  * @extends Roo.bootstrap.Component
22640  * @children Roo.bootstrap.Component
22641  * Bootstrap TabPanel class
22642  * @cfg {Boolean} active panel active
22643  * @cfg {String} html panel content
22644  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22645  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22646  * @cfg {String} href click to link..
22647  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22648  * 
22649  * 
22650  * @constructor
22651  * Create a new TabPanel
22652  * @param {Object} config The config object
22653  */
22654
22655 Roo.bootstrap.TabPanel = function(config){
22656     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22657     this.addEvents({
22658         /**
22659              * @event changed
22660              * Fires when the active status changes
22661              * @param {Roo.bootstrap.TabPanel} this
22662              * @param {Boolean} state the new state
22663             
22664          */
22665         'changed': true,
22666         /**
22667              * @event beforedeactivate
22668              * Fires before a tab is de-activated - can be used to do validation on a form.
22669              * @param {Roo.bootstrap.TabPanel} this
22670              * @return {Boolean} false if there is an error
22671             
22672          */
22673         'beforedeactivate': true
22674      });
22675     
22676     this.tabId = this.tabId || Roo.id();
22677   
22678 };
22679
22680 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22681     
22682     active: false,
22683     html: false,
22684     tabId: false,
22685     navId : false,
22686     href : '',
22687     touchSlide : false,
22688     getAutoCreate : function(){
22689         
22690         
22691         var cfg = {
22692             tag: 'div',
22693             // item is needed for carousel - not sure if it has any effect otherwise
22694             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22695             html: this.html || ''
22696         };
22697         
22698         if(this.active){
22699             cfg.cls += ' active';
22700         }
22701         
22702         if(this.tabId){
22703             cfg.tabId = this.tabId;
22704         }
22705         
22706         
22707         
22708         return cfg;
22709     },
22710     
22711     initEvents:  function()
22712     {
22713         var p = this.parent();
22714         
22715         this.navId = this.navId || p.navId;
22716         
22717         if (typeof(this.navId) != 'undefined') {
22718             // not really needed.. but just in case.. parent should be a NavGroup.
22719             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22720             
22721             tg.register(this);
22722             
22723             var i = tg.tabs.length - 1;
22724             
22725             if(this.active && tg.bullets > 0 && i < tg.bullets){
22726                 tg.setActiveBullet(i);
22727             }
22728         }
22729         
22730         this.el.on('click', this.onClick, this);
22731         
22732         if(Roo.isTouch && this.touchSlide){
22733             this.el.on("touchstart", this.onTouchStart, this);
22734             this.el.on("touchmove", this.onTouchMove, this);
22735             this.el.on("touchend", this.onTouchEnd, this);
22736         }
22737         
22738     },
22739     
22740     onRender : function(ct, position)
22741     {
22742         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22743     },
22744     
22745     setActive : function(state)
22746     {
22747         Roo.log("panel - set active " + this.tabId + "=" + state);
22748         
22749         this.active = state;
22750         if (!state) {
22751             this.el.removeClass('active');
22752             
22753         } else  if (!this.el.hasClass('active')) {
22754             this.el.addClass('active');
22755         }
22756         
22757         this.fireEvent('changed', this, state);
22758     },
22759     
22760     onClick : function(e)
22761     {
22762         e.preventDefault();
22763         
22764         if(!this.href.length){
22765             return;
22766         }
22767         
22768         window.location.href = this.href;
22769     },
22770     
22771     startX : 0,
22772     startY : 0,
22773     endX : 0,
22774     endY : 0,
22775     swiping : false,
22776     
22777     onTouchStart : function(e)
22778     {
22779         this.swiping = false;
22780         
22781         this.startX = e.browserEvent.touches[0].clientX;
22782         this.startY = e.browserEvent.touches[0].clientY;
22783     },
22784     
22785     onTouchMove : function(e)
22786     {
22787         this.swiping = true;
22788         
22789         this.endX = e.browserEvent.touches[0].clientX;
22790         this.endY = e.browserEvent.touches[0].clientY;
22791     },
22792     
22793     onTouchEnd : function(e)
22794     {
22795         if(!this.swiping){
22796             this.onClick(e);
22797             return;
22798         }
22799         
22800         var tabGroup = this.parent();
22801         
22802         if(this.endX > this.startX){ // swiping right
22803             tabGroup.showPanelPrev();
22804             return;
22805         }
22806         
22807         if(this.startX > this.endX){ // swiping left
22808             tabGroup.showPanelNext();
22809             return;
22810         }
22811     }
22812     
22813     
22814 });
22815  
22816
22817  
22818
22819  /*
22820  * - LGPL
22821  *
22822  * DateField
22823  * 
22824  */
22825
22826 /**
22827  * @class Roo.bootstrap.form.DateField
22828  * @extends Roo.bootstrap.form.Input
22829  * Bootstrap DateField class
22830  * @cfg {Number} weekStart default 0
22831  * @cfg {String} viewMode default empty, (months|years)
22832  * @cfg {String} minViewMode default empty, (months|years)
22833  * @cfg {Number} startDate default -Infinity
22834  * @cfg {Number} endDate default Infinity
22835  * @cfg {Boolean} todayHighlight default false
22836  * @cfg {Boolean} todayBtn default false
22837  * @cfg {Boolean} calendarWeeks default false
22838  * @cfg {Object} daysOfWeekDisabled default empty
22839  * @cfg {Boolean} singleMode default false (true | false)
22840  * 
22841  * @cfg {Boolean} keyboardNavigation default true
22842  * @cfg {String} language default en
22843  * 
22844  * @constructor
22845  * Create a new DateField
22846  * @param {Object} config The config object
22847  */
22848
22849 Roo.bootstrap.form.DateField = function(config){
22850     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22851      this.addEvents({
22852             /**
22853              * @event show
22854              * Fires when this field show.
22855              * @param {Roo.bootstrap.form.DateField} this
22856              * @param {Mixed} date The date value
22857              */
22858             show : true,
22859             /**
22860              * @event show
22861              * Fires when this field hide.
22862              * @param {Roo.bootstrap.form.DateField} this
22863              * @param {Mixed} date The date value
22864              */
22865             hide : true,
22866             /**
22867              * @event select
22868              * Fires when select a date.
22869              * @param {Roo.bootstrap.form.DateField} this
22870              * @param {Mixed} date The date value
22871              */
22872             select : true,
22873             /**
22874              * @event beforeselect
22875              * Fires when before select a date.
22876              * @param {Roo.bootstrap.form.DateField} this
22877              * @param {Mixed} date The date value
22878              */
22879             beforeselect : true
22880         });
22881 };
22882
22883 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22884     
22885     /**
22886      * @cfg {String} format
22887      * The default date format string which can be overriden for localization support.  The format must be
22888      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22889      */
22890     format : "m/d/y",
22891     /**
22892      * @cfg {String} altFormats
22893      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22894      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22895      */
22896     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22897     
22898     weekStart : 0,
22899     
22900     viewMode : '',
22901     
22902     minViewMode : '',
22903     
22904     todayHighlight : false,
22905     
22906     todayBtn: false,
22907     
22908     language: 'en',
22909     
22910     keyboardNavigation: true,
22911     
22912     calendarWeeks: false,
22913     
22914     startDate: -Infinity,
22915     
22916     endDate: Infinity,
22917     
22918     daysOfWeekDisabled: [],
22919     
22920     _events: [],
22921     
22922     singleMode : false,
22923     
22924     UTCDate: function()
22925     {
22926         return new Date(Date.UTC.apply(Date, arguments));
22927     },
22928     
22929     UTCToday: function()
22930     {
22931         var today = new Date();
22932         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22933     },
22934     
22935     getDate: function() {
22936             var d = this.getUTCDate();
22937             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22938     },
22939     
22940     getUTCDate: function() {
22941             return this.date;
22942     },
22943     
22944     setDate: function(d) {
22945             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22946     },
22947     
22948     setUTCDate: function(d) {
22949             this.date = d;
22950             this.setValue(this.formatDate(this.date));
22951     },
22952         
22953     onRender: function(ct, position)
22954     {
22955         
22956         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22957         
22958         this.language = this.language || 'en';
22959         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22960         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22961         
22962         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22963         this.format = this.format || 'm/d/y';
22964         this.isInline = false;
22965         this.isInput = true;
22966         this.component = this.el.select('.add-on', true).first() || false;
22967         this.component = (this.component && this.component.length === 0) ? false : this.component;
22968         this.hasInput = this.component && this.inputEl().length;
22969         
22970         if (typeof(this.minViewMode === 'string')) {
22971             switch (this.minViewMode) {
22972                 case 'months':
22973                     this.minViewMode = 1;
22974                     break;
22975                 case 'years':
22976                     this.minViewMode = 2;
22977                     break;
22978                 default:
22979                     this.minViewMode = 0;
22980                     break;
22981             }
22982         }
22983         
22984         if (typeof(this.viewMode === 'string')) {
22985             switch (this.viewMode) {
22986                 case 'months':
22987                     this.viewMode = 1;
22988                     break;
22989                 case 'years':
22990                     this.viewMode = 2;
22991                     break;
22992                 default:
22993                     this.viewMode = 0;
22994                     break;
22995             }
22996         }
22997                 
22998         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22999         
23000 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23001         
23002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23003         
23004         this.picker().on('mousedown', this.onMousedown, this);
23005         this.picker().on('click', this.onClick, this);
23006         
23007         this.picker().addClass('datepicker-dropdown');
23008         
23009         this.startViewMode = this.viewMode;
23010         
23011         if(this.singleMode){
23012             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23013                 v.setVisibilityMode(Roo.Element.DISPLAY);
23014                 v.hide();
23015             });
23016             
23017             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23018                 v.setStyle('width', '189px');
23019             });
23020         }
23021         
23022         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23023             if(!this.calendarWeeks){
23024                 v.remove();
23025                 return;
23026             }
23027             
23028             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23029             v.attr('colspan', function(i, val){
23030                 return parseInt(val) + 1;
23031             });
23032         });
23033                         
23034         
23035         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23036         
23037         this.setStartDate(this.startDate);
23038         this.setEndDate(this.endDate);
23039         
23040         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23041         
23042         this.fillDow();
23043         this.fillMonths();
23044         this.update();
23045         this.showMode();
23046         
23047         if(this.isInline) {
23048             this.showPopup();
23049         }
23050     },
23051     
23052     picker : function()
23053     {
23054         return this.pickerEl;
23055 //        return this.el.select('.datepicker', true).first();
23056     },
23057     
23058     fillDow: function()
23059     {
23060         var dowCnt = this.weekStart;
23061         
23062         var dow = {
23063             tag: 'tr',
23064             cn: [
23065                 
23066             ]
23067         };
23068         
23069         if(this.calendarWeeks){
23070             dow.cn.push({
23071                 tag: 'th',
23072                 cls: 'cw',
23073                 html: '&nbsp;'
23074             })
23075         }
23076         
23077         while (dowCnt < this.weekStart + 7) {
23078             dow.cn.push({
23079                 tag: 'th',
23080                 cls: 'dow',
23081                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23082             });
23083         }
23084         
23085         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23086     },
23087     
23088     fillMonths: function()
23089     {    
23090         var i = 0;
23091         var months = this.picker().select('>.datepicker-months td', true).first();
23092         
23093         months.dom.innerHTML = '';
23094         
23095         while (i < 12) {
23096             var month = {
23097                 tag: 'span',
23098                 cls: 'month',
23099                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23100             };
23101             
23102             months.createChild(month);
23103         }
23104         
23105     },
23106     
23107     update: function()
23108     {
23109         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;
23110         
23111         if (this.date < this.startDate) {
23112             this.viewDate = new Date(this.startDate);
23113         } else if (this.date > this.endDate) {
23114             this.viewDate = new Date(this.endDate);
23115         } else {
23116             this.viewDate = new Date(this.date);
23117         }
23118         
23119         this.fill();
23120     },
23121     
23122     fill: function() 
23123     {
23124         var d = new Date(this.viewDate),
23125                 year = d.getUTCFullYear(),
23126                 month = d.getUTCMonth(),
23127                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23128                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23129                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23130                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23131                 currentDate = this.date && this.date.valueOf(),
23132                 today = this.UTCToday();
23133         
23134         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23135         
23136 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23137         
23138 //        this.picker.select('>tfoot th.today').
23139 //                                              .text(dates[this.language].today)
23140 //                                              .toggle(this.todayBtn !== false);
23141     
23142         this.updateNavArrows();
23143         this.fillMonths();
23144                                                 
23145         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23146         
23147         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23148          
23149         prevMonth.setUTCDate(day);
23150         
23151         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23152         
23153         var nextMonth = new Date(prevMonth);
23154         
23155         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23156         
23157         nextMonth = nextMonth.valueOf();
23158         
23159         var fillMonths = false;
23160         
23161         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23162         
23163         while(prevMonth.valueOf() <= nextMonth) {
23164             var clsName = '';
23165             
23166             if (prevMonth.getUTCDay() === this.weekStart) {
23167                 if(fillMonths){
23168                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23169                 }
23170                     
23171                 fillMonths = {
23172                     tag: 'tr',
23173                     cn: []
23174                 };
23175                 
23176                 if(this.calendarWeeks){
23177                     // ISO 8601: First week contains first thursday.
23178                     // ISO also states week starts on Monday, but we can be more abstract here.
23179                     var
23180                     // Start of current week: based on weekstart/current date
23181                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23182                     // Thursday of this week
23183                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23184                     // First Thursday of year, year from thursday
23185                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23186                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23187                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23188                     
23189                     fillMonths.cn.push({
23190                         tag: 'td',
23191                         cls: 'cw',
23192                         html: calWeek
23193                     });
23194                 }
23195             }
23196             
23197             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23198                 clsName += ' old';
23199             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23200                 clsName += ' new';
23201             }
23202             if (this.todayHighlight &&
23203                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23204                 prevMonth.getUTCMonth() == today.getMonth() &&
23205                 prevMonth.getUTCDate() == today.getDate()) {
23206                 clsName += ' today';
23207             }
23208             
23209             if (currentDate && prevMonth.valueOf() === currentDate) {
23210                 clsName += ' active';
23211             }
23212             
23213             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23214                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23215                     clsName += ' disabled';
23216             }
23217             
23218             fillMonths.cn.push({
23219                 tag: 'td',
23220                 cls: 'day ' + clsName,
23221                 html: prevMonth.getDate()
23222             });
23223             
23224             prevMonth.setDate(prevMonth.getDate()+1);
23225         }
23226           
23227         var currentYear = this.date && this.date.getUTCFullYear();
23228         var currentMonth = this.date && this.date.getUTCMonth();
23229         
23230         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23231         
23232         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23233             v.removeClass('active');
23234             
23235             if(currentYear === year && k === currentMonth){
23236                 v.addClass('active');
23237             }
23238             
23239             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23240                 v.addClass('disabled');
23241             }
23242             
23243         });
23244         
23245         
23246         year = parseInt(year/10, 10) * 10;
23247         
23248         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23249         
23250         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23251         
23252         year -= 1;
23253         for (var i = -1; i < 11; i++) {
23254             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23255                 tag: 'span',
23256                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23257                 html: year
23258             });
23259             
23260             year += 1;
23261         }
23262     },
23263     
23264     showMode: function(dir) 
23265     {
23266         if (dir) {
23267             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23268         }
23269         
23270         Roo.each(this.picker().select('>div',true).elements, function(v){
23271             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23272             v.hide();
23273         });
23274         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23275     },
23276     
23277     place: function()
23278     {
23279         if(this.isInline) {
23280             return;
23281         }
23282         
23283         this.picker().removeClass(['bottom', 'top']);
23284         
23285         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23286             /*
23287              * place to the top of element!
23288              *
23289              */
23290             
23291             this.picker().addClass('top');
23292             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23293             
23294             return;
23295         }
23296         
23297         this.picker().addClass('bottom');
23298         
23299         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23300     },
23301     
23302     parseDate : function(value)
23303     {
23304         if(!value || value instanceof Date){
23305             return value;
23306         }
23307         var v = Date.parseDate(value, this.format);
23308         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23309             v = Date.parseDate(value, 'Y-m-d');
23310         }
23311         if(!v && this.altFormats){
23312             if(!this.altFormatsArray){
23313                 this.altFormatsArray = this.altFormats.split("|");
23314             }
23315             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23316                 v = Date.parseDate(value, this.altFormatsArray[i]);
23317             }
23318         }
23319         return v;
23320     },
23321     
23322     formatDate : function(date, fmt)
23323     {   
23324         return (!date || !(date instanceof Date)) ?
23325         date : date.dateFormat(fmt || this.format);
23326     },
23327     
23328     onFocus : function()
23329     {
23330         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23331         this.showPopup();
23332     },
23333     
23334     onBlur : function()
23335     {
23336         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23337         
23338         var d = this.inputEl().getValue();
23339         
23340         this.setValue(d);
23341                 
23342         this.hidePopup();
23343     },
23344     
23345     showPopup : function()
23346     {
23347         this.picker().show();
23348         this.update();
23349         this.place();
23350         
23351         this.fireEvent('showpopup', this, this.date);
23352     },
23353     
23354     hidePopup : function()
23355     {
23356         if(this.isInline) {
23357             return;
23358         }
23359         this.picker().hide();
23360         this.viewMode = this.startViewMode;
23361         this.showMode();
23362         
23363         this.fireEvent('hidepopup', this, this.date);
23364         
23365     },
23366     
23367     onMousedown: function(e)
23368     {
23369         e.stopPropagation();
23370         e.preventDefault();
23371     },
23372     
23373     keyup: function(e)
23374     {
23375         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23376         this.update();
23377     },
23378
23379     setValue: function(v)
23380     {
23381         if(this.fireEvent('beforeselect', this, v) !== false){
23382             var d = new Date(this.parseDate(v) ).clearTime();
23383         
23384             if(isNaN(d.getTime())){
23385                 this.date = this.viewDate = '';
23386                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23387                 return;
23388             }
23389
23390             v = this.formatDate(d);
23391
23392             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23393
23394             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23395
23396             this.update();
23397
23398             this.fireEvent('select', this, this.date);
23399         }
23400     },
23401     
23402     getValue: function()
23403     {
23404         return this.formatDate(this.date);
23405     },
23406     
23407     fireKey: function(e)
23408     {
23409         if (!this.picker().isVisible()){
23410             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23411                 this.showPopup();
23412             }
23413             return;
23414         }
23415         
23416         var dateChanged = false,
23417         dir, day, month,
23418         newDate, newViewDate;
23419         
23420         switch(e.keyCode){
23421             case 27: // escape
23422                 this.hidePopup();
23423                 e.preventDefault();
23424                 break;
23425             case 37: // left
23426             case 39: // right
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 37 ? -1 : 1;
23431                 
23432                 if (e.ctrlKey){
23433                     newDate = this.moveYear(this.date, dir);
23434                     newViewDate = this.moveYear(this.viewDate, dir);
23435                 } else if (e.shiftKey){
23436                     newDate = this.moveMonth(this.date, dir);
23437                     newViewDate = this.moveMonth(this.viewDate, dir);
23438                 } else {
23439                     newDate = new Date(this.date);
23440                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23441                     newViewDate = new Date(this.viewDate);
23442                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23443                 }
23444                 if (this.dateWithinRange(newDate)){
23445                     this.date = newDate;
23446                     this.viewDate = newViewDate;
23447                     this.setValue(this.formatDate(this.date));
23448 //                    this.update();
23449                     e.preventDefault();
23450                     dateChanged = true;
23451                 }
23452                 break;
23453             case 38: // up
23454             case 40: // down
23455                 if (!this.keyboardNavigation) {
23456                     break;
23457                 }
23458                 dir = e.keyCode == 38 ? -1 : 1;
23459                 if (e.ctrlKey){
23460                     newDate = this.moveYear(this.date, dir);
23461                     newViewDate = this.moveYear(this.viewDate, dir);
23462                 } else if (e.shiftKey){
23463                     newDate = this.moveMonth(this.date, dir);
23464                     newViewDate = this.moveMonth(this.viewDate, dir);
23465                 } else {
23466                     newDate = new Date(this.date);
23467                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23468                     newViewDate = new Date(this.viewDate);
23469                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23470                 }
23471                 if (this.dateWithinRange(newDate)){
23472                     this.date = newDate;
23473                     this.viewDate = newViewDate;
23474                     this.setValue(this.formatDate(this.date));
23475 //                    this.update();
23476                     e.preventDefault();
23477                     dateChanged = true;
23478                 }
23479                 break;
23480             case 13: // enter
23481                 this.setValue(this.formatDate(this.date));
23482                 this.hidePopup();
23483                 e.preventDefault();
23484                 break;
23485             case 9: // tab
23486                 this.setValue(this.formatDate(this.date));
23487                 this.hidePopup();
23488                 break;
23489             case 16: // shift
23490             case 17: // ctrl
23491             case 18: // alt
23492                 break;
23493             default :
23494                 this.hidePopup();
23495                 
23496         }
23497     },
23498     
23499     
23500     onClick: function(e) 
23501     {
23502         e.stopPropagation();
23503         e.preventDefault();
23504         
23505         var target = e.getTarget();
23506         
23507         if(target.nodeName.toLowerCase() === 'i'){
23508             target = Roo.get(target).dom.parentNode;
23509         }
23510         
23511         var nodeName = target.nodeName;
23512         var className = target.className;
23513         var html = target.innerHTML;
23514         //Roo.log(nodeName);
23515         
23516         switch(nodeName.toLowerCase()) {
23517             case 'th':
23518                 switch(className) {
23519                     case 'switch':
23520                         this.showMode(1);
23521                         break;
23522                     case 'prev':
23523                     case 'next':
23524                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23525                         switch(this.viewMode){
23526                                 case 0:
23527                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23528                                         break;
23529                                 case 1:
23530                                 case 2:
23531                                         this.viewDate = this.moveYear(this.viewDate, dir);
23532                                         break;
23533                         }
23534                         this.fill();
23535                         break;
23536                     case 'today':
23537                         var date = new Date();
23538                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23539 //                        this.fill()
23540                         this.setValue(this.formatDate(this.date));
23541                         
23542                         this.hidePopup();
23543                         break;
23544                 }
23545                 break;
23546             case 'span':
23547                 if (className.indexOf('disabled') < 0) {
23548                 if (!this.viewDate) {
23549                     this.viewDate = new Date();
23550                 }
23551                 this.viewDate.setUTCDate(1);
23552                     if (className.indexOf('month') > -1) {
23553                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23554                     } else {
23555                         var year = parseInt(html, 10) || 0;
23556                         this.viewDate.setUTCFullYear(year);
23557                         
23558                     }
23559                     
23560                     if(this.singleMode){
23561                         this.setValue(this.formatDate(this.viewDate));
23562                         this.hidePopup();
23563                         return;
23564                     }
23565                     
23566                     this.showMode(-1);
23567                     this.fill();
23568                 }
23569                 break;
23570                 
23571             case 'td':
23572                 //Roo.log(className);
23573                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23574                     var day = parseInt(html, 10) || 1;
23575                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23576                         month = (this.viewDate || new Date()).getUTCMonth();
23577
23578                     if (className.indexOf('old') > -1) {
23579                         if(month === 0 ){
23580                             month = 11;
23581                             year -= 1;
23582                         }else{
23583                             month -= 1;
23584                         }
23585                     } else if (className.indexOf('new') > -1) {
23586                         if (month == 11) {
23587                             month = 0;
23588                             year += 1;
23589                         } else {
23590                             month += 1;
23591                         }
23592                     }
23593                     //Roo.log([year,month,day]);
23594                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23595                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23596 //                    this.fill();
23597                     //Roo.log(this.formatDate(this.date));
23598                     this.setValue(this.formatDate(this.date));
23599                     this.hidePopup();
23600                 }
23601                 break;
23602         }
23603     },
23604     
23605     setStartDate: function(startDate)
23606     {
23607         this.startDate = startDate || -Infinity;
23608         if (this.startDate !== -Infinity) {
23609             this.startDate = this.parseDate(this.startDate);
23610         }
23611         this.update();
23612         this.updateNavArrows();
23613     },
23614
23615     setEndDate: function(endDate)
23616     {
23617         this.endDate = endDate || Infinity;
23618         if (this.endDate !== Infinity) {
23619             this.endDate = this.parseDate(this.endDate);
23620         }
23621         this.update();
23622         this.updateNavArrows();
23623     },
23624     
23625     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23626     {
23627         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23628         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23629             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23630         }
23631         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23632             return parseInt(d, 10);
23633         });
23634         this.update();
23635         this.updateNavArrows();
23636     },
23637     
23638     updateNavArrows: function() 
23639     {
23640         if(this.singleMode){
23641             return;
23642         }
23643         
23644         var d = new Date(this.viewDate),
23645         year = d.getUTCFullYear(),
23646         month = d.getUTCMonth();
23647         
23648         Roo.each(this.picker().select('.prev', true).elements, function(v){
23649             v.show();
23650             switch (this.viewMode) {
23651                 case 0:
23652
23653                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23654                         v.hide();
23655                     }
23656                     break;
23657                 case 1:
23658                 case 2:
23659                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23660                         v.hide();
23661                     }
23662                     break;
23663             }
23664         });
23665         
23666         Roo.each(this.picker().select('.next', true).elements, function(v){
23667             v.show();
23668             switch (this.viewMode) {
23669                 case 0:
23670
23671                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23672                         v.hide();
23673                     }
23674                     break;
23675                 case 1:
23676                 case 2:
23677                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23678                         v.hide();
23679                     }
23680                     break;
23681             }
23682         })
23683     },
23684     
23685     moveMonth: function(date, dir)
23686     {
23687         if (!dir) {
23688             return date;
23689         }
23690         var new_date = new Date(date.valueOf()),
23691         day = new_date.getUTCDate(),
23692         month = new_date.getUTCMonth(),
23693         mag = Math.abs(dir),
23694         new_month, test;
23695         dir = dir > 0 ? 1 : -1;
23696         if (mag == 1){
23697             test = dir == -1
23698             // If going back one month, make sure month is not current month
23699             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23700             ? function(){
23701                 return new_date.getUTCMonth() == month;
23702             }
23703             // If going forward one month, make sure month is as expected
23704             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23705             : function(){
23706                 return new_date.getUTCMonth() != new_month;
23707             };
23708             new_month = month + dir;
23709             new_date.setUTCMonth(new_month);
23710             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23711             if (new_month < 0 || new_month > 11) {
23712                 new_month = (new_month + 12) % 12;
23713             }
23714         } else {
23715             // For magnitudes >1, move one month at a time...
23716             for (var i=0; i<mag; i++) {
23717                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23718                 new_date = this.moveMonth(new_date, dir);
23719             }
23720             // ...then reset the day, keeping it in the new month
23721             new_month = new_date.getUTCMonth();
23722             new_date.setUTCDate(day);
23723             test = function(){
23724                 return new_month != new_date.getUTCMonth();
23725             };
23726         }
23727         // Common date-resetting loop -- if date is beyond end of month, make it
23728         // end of month
23729         while (test()){
23730             new_date.setUTCDate(--day);
23731             new_date.setUTCMonth(new_month);
23732         }
23733         return new_date;
23734     },
23735
23736     moveYear: function(date, dir)
23737     {
23738         return this.moveMonth(date, dir*12);
23739     },
23740
23741     dateWithinRange: function(date)
23742     {
23743         return date >= this.startDate && date <= this.endDate;
23744     },
23745
23746     
23747     remove: function() 
23748     {
23749         this.picker().remove();
23750     },
23751     
23752     validateValue : function(value)
23753     {
23754         if(this.getVisibilityEl().hasClass('hidden')){
23755             return true;
23756         }
23757         
23758         if(value.length < 1)  {
23759             if(this.allowBlank){
23760                 return true;
23761             }
23762             return false;
23763         }
23764         
23765         if(value.length < this.minLength){
23766             return false;
23767         }
23768         if(value.length > this.maxLength){
23769             return false;
23770         }
23771         if(this.vtype){
23772             var vt = Roo.form.VTypes;
23773             if(!vt[this.vtype](value, this)){
23774                 return false;
23775             }
23776         }
23777         if(typeof this.validator == "function"){
23778             var msg = this.validator(value);
23779             if(msg !== true){
23780                 return false;
23781             }
23782         }
23783         
23784         if(this.regex && !this.regex.test(value)){
23785             return false;
23786         }
23787         
23788         if(typeof(this.parseDate(value)) == 'undefined'){
23789             return false;
23790         }
23791         
23792         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23793             return false;
23794         }      
23795         
23796         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23797             return false;
23798         } 
23799         
23800         
23801         return true;
23802     },
23803     
23804     reset : function()
23805     {
23806         this.date = this.viewDate = '';
23807         
23808         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23809     }
23810    
23811 });
23812
23813 Roo.apply(Roo.bootstrap.form.DateField,  {
23814     
23815     head : {
23816         tag: 'thead',
23817         cn: [
23818         {
23819             tag: 'tr',
23820             cn: [
23821             {
23822                 tag: 'th',
23823                 cls: 'prev',
23824                 html: '<i class="fa fa-arrow-left"/>'
23825             },
23826             {
23827                 tag: 'th',
23828                 cls: 'switch',
23829                 colspan: '5'
23830             },
23831             {
23832                 tag: 'th',
23833                 cls: 'next',
23834                 html: '<i class="fa fa-arrow-right"/>'
23835             }
23836
23837             ]
23838         }
23839         ]
23840     },
23841     
23842     content : {
23843         tag: 'tbody',
23844         cn: [
23845         {
23846             tag: 'tr',
23847             cn: [
23848             {
23849                 tag: 'td',
23850                 colspan: '7'
23851             }
23852             ]
23853         }
23854         ]
23855     },
23856     
23857     footer : {
23858         tag: 'tfoot',
23859         cn: [
23860         {
23861             tag: 'tr',
23862             cn: [
23863             {
23864                 tag: 'th',
23865                 colspan: '7',
23866                 cls: 'today'
23867             }
23868                     
23869             ]
23870         }
23871         ]
23872     },
23873     
23874     dates:{
23875         en: {
23876             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23877             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23878             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23879             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23880             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23881             today: "Today"
23882         }
23883     },
23884     
23885     modes: [
23886     {
23887         clsName: 'days',
23888         navFnc: 'Month',
23889         navStep: 1
23890     },
23891     {
23892         clsName: 'months',
23893         navFnc: 'FullYear',
23894         navStep: 1
23895     },
23896     {
23897         clsName: 'years',
23898         navFnc: 'FullYear',
23899         navStep: 10
23900     }]
23901 });
23902
23903 Roo.apply(Roo.bootstrap.form.DateField,  {
23904   
23905     template : {
23906         tag: 'div',
23907         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23908         cn: [
23909         {
23910             tag: 'div',
23911             cls: 'datepicker-days',
23912             cn: [
23913             {
23914                 tag: 'table',
23915                 cls: 'table-condensed',
23916                 cn:[
23917                 Roo.bootstrap.form.DateField.head,
23918                 {
23919                     tag: 'tbody'
23920                 },
23921                 Roo.bootstrap.form.DateField.footer
23922                 ]
23923             }
23924             ]
23925         },
23926         {
23927             tag: 'div',
23928             cls: 'datepicker-months',
23929             cn: [
23930             {
23931                 tag: 'table',
23932                 cls: 'table-condensed',
23933                 cn:[
23934                 Roo.bootstrap.form.DateField.head,
23935                 Roo.bootstrap.form.DateField.content,
23936                 Roo.bootstrap.form.DateField.footer
23937                 ]
23938             }
23939             ]
23940         },
23941         {
23942             tag: 'div',
23943             cls: 'datepicker-years',
23944             cn: [
23945             {
23946                 tag: 'table',
23947                 cls: 'table-condensed',
23948                 cn:[
23949                 Roo.bootstrap.form.DateField.head,
23950                 Roo.bootstrap.form.DateField.content,
23951                 Roo.bootstrap.form.DateField.footer
23952                 ]
23953             }
23954             ]
23955         }
23956         ]
23957     }
23958 });
23959
23960  
23961
23962  /*
23963  * - LGPL
23964  *
23965  * TimeField
23966  * 
23967  */
23968
23969 /**
23970  * @class Roo.bootstrap.form.TimeField
23971  * @extends Roo.bootstrap.form.Input
23972  * Bootstrap DateField class
23973  * 
23974  * 
23975  * @constructor
23976  * Create a new TimeField
23977  * @param {Object} config The config object
23978  */
23979
23980 Roo.bootstrap.form.TimeField = function(config){
23981     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23982     this.addEvents({
23983             /**
23984              * @event show
23985              * Fires when this field show.
23986              * @param {Roo.bootstrap.form.DateField} thisthis
23987              * @param {Mixed} date The date value
23988              */
23989             show : true,
23990             /**
23991              * @event show
23992              * Fires when this field hide.
23993              * @param {Roo.bootstrap.form.DateField} this
23994              * @param {Mixed} date The date value
23995              */
23996             hide : true,
23997             /**
23998              * @event select
23999              * Fires when select a date.
24000              * @param {Roo.bootstrap.form.DateField} this
24001              * @param {Mixed} date The date value
24002              */
24003             select : true
24004         });
24005 };
24006
24007 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24008     
24009     /**
24010      * @cfg {String} format
24011      * The default time format string which can be overriden for localization support.  The format must be
24012      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24013      */
24014     format : "H:i",
24015
24016     getAutoCreate : function()
24017     {
24018         this.after = '<i class="fa far fa-clock"></i>';
24019         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24020         
24021          
24022     },
24023     onRender: function(ct, position)
24024     {
24025         
24026         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24027                 
24028         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24029         
24030         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24031         
24032         this.pop = this.picker().select('>.datepicker-time',true).first();
24033         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24034         
24035         this.picker().on('mousedown', this.onMousedown, this);
24036         this.picker().on('click', this.onClick, this);
24037         
24038         this.picker().addClass('datepicker-dropdown');
24039     
24040         this.fillTime();
24041         this.update();
24042             
24043         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24044         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24045         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24046         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24047         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24048         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24049
24050     },
24051     
24052     fireKey: function(e){
24053         if (!this.picker().isVisible()){
24054             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24055                 this.show();
24056             }
24057             return;
24058         }
24059
24060         e.preventDefault();
24061         
24062         switch(e.keyCode){
24063             case 27: // escape
24064                 this.hide();
24065                 break;
24066             case 37: // left
24067             case 39: // right
24068                 this.onTogglePeriod();
24069                 break;
24070             case 38: // up
24071                 this.onIncrementMinutes();
24072                 break;
24073             case 40: // down
24074                 this.onDecrementMinutes();
24075                 break;
24076             case 13: // enter
24077             case 9: // tab
24078                 this.setTime();
24079                 break;
24080         }
24081     },
24082     
24083     onClick: function(e) {
24084         e.stopPropagation();
24085         e.preventDefault();
24086     },
24087     
24088     picker : function()
24089     {
24090         return this.pickerEl;
24091     },
24092     
24093     fillTime: function()
24094     {    
24095         var time = this.pop.select('tbody', true).first();
24096         
24097         time.dom.innerHTML = '';
24098         
24099         time.createChild({
24100             tag: 'tr',
24101             cn: [
24102                 {
24103                     tag: 'td',
24104                     cn: [
24105                         {
24106                             tag: 'a',
24107                             href: '#',
24108                             cls: 'btn',
24109                             cn: [
24110                                 {
24111                                     tag: 'i',
24112                                     cls: 'hours-up fa fas fa-chevron-up'
24113                                 }
24114                             ]
24115                         } 
24116                     ]
24117                 },
24118                 {
24119                     tag: 'td',
24120                     cls: 'separator'
24121                 },
24122                 {
24123                     tag: 'td',
24124                     cn: [
24125                         {
24126                             tag: 'a',
24127                             href: '#',
24128                             cls: 'btn',
24129                             cn: [
24130                                 {
24131                                     tag: 'i',
24132                                     cls: 'minutes-up fa fas fa-chevron-up'
24133                                 }
24134                             ]
24135                         }
24136                     ]
24137                 },
24138                 {
24139                     tag: 'td',
24140                     cls: 'separator'
24141                 }
24142             ]
24143         });
24144         
24145         time.createChild({
24146             tag: 'tr',
24147             cn: [
24148                 {
24149                     tag: 'td',
24150                     cn: [
24151                         {
24152                             tag: 'span',
24153                             cls: 'timepicker-hour',
24154                             html: '00'
24155                         }  
24156                     ]
24157                 },
24158                 {
24159                     tag: 'td',
24160                     cls: 'separator',
24161                     html: ':'
24162                 },
24163                 {
24164                     tag: 'td',
24165                     cn: [
24166                         {
24167                             tag: 'span',
24168                             cls: 'timepicker-minute',
24169                             html: '00'
24170                         }  
24171                     ]
24172                 },
24173                 {
24174                     tag: 'td',
24175                     cls: 'separator'
24176                 },
24177                 {
24178                     tag: 'td',
24179                     cn: [
24180                         {
24181                             tag: 'button',
24182                             type: 'button',
24183                             cls: 'btn btn-primary period',
24184                             html: 'AM'
24185                             
24186                         }
24187                     ]
24188                 }
24189             ]
24190         });
24191         
24192         time.createChild({
24193             tag: 'tr',
24194             cn: [
24195                 {
24196                     tag: 'td',
24197                     cn: [
24198                         {
24199                             tag: 'a',
24200                             href: '#',
24201                             cls: 'btn',
24202                             cn: [
24203                                 {
24204                                     tag: 'span',
24205                                     cls: 'hours-down fa fas fa-chevron-down'
24206                                 }
24207                             ]
24208                         }
24209                     ]
24210                 },
24211                 {
24212                     tag: 'td',
24213                     cls: 'separator'
24214                 },
24215                 {
24216                     tag: 'td',
24217                     cn: [
24218                         {
24219                             tag: 'a',
24220                             href: '#',
24221                             cls: 'btn',
24222                             cn: [
24223                                 {
24224                                     tag: 'span',
24225                                     cls: 'minutes-down fa fas fa-chevron-down'
24226                                 }
24227                             ]
24228                         }
24229                     ]
24230                 },
24231                 {
24232                     tag: 'td',
24233                     cls: 'separator'
24234                 }
24235             ]
24236         });
24237         
24238     },
24239     
24240     update: function()
24241     {
24242         
24243         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24244         
24245         this.fill();
24246     },
24247     
24248     fill: function() 
24249     {
24250         var hours = this.time.getHours();
24251         var minutes = this.time.getMinutes();
24252         var period = 'AM';
24253         
24254         if(hours > 11){
24255             period = 'PM';
24256         }
24257         
24258         if(hours == 0){
24259             hours = 12;
24260         }
24261         
24262         
24263         if(hours > 12){
24264             hours = hours - 12;
24265         }
24266         
24267         if(hours < 10){
24268             hours = '0' + hours;
24269         }
24270         
24271         if(minutes < 10){
24272             minutes = '0' + minutes;
24273         }
24274         
24275         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24276         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24277         this.pop.select('button', true).first().dom.innerHTML = period;
24278         
24279     },
24280     
24281     place: function()
24282     {   
24283         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24284         
24285         var cls = ['bottom'];
24286         
24287         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24288             cls.pop();
24289             cls.push('top');
24290         }
24291         
24292         cls.push('right');
24293         
24294         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24295             cls.pop();
24296             cls.push('left');
24297         }
24298         //this.picker().setXY(20000,20000);
24299         this.picker().addClass(cls.join('-'));
24300         
24301         var _this = this;
24302         
24303         Roo.each(cls, function(c){
24304             if(c == 'bottom'){
24305                 (function() {
24306                  //  
24307                 }).defer(200);
24308                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24309                 //_this.picker().setTop(_this.inputEl().getHeight());
24310                 return;
24311             }
24312             if(c == 'top'){
24313                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24314                 
24315                 //_this.picker().setTop(0 - _this.picker().getHeight());
24316                 return;
24317             }
24318             /*
24319             if(c == 'left'){
24320                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24321                 return;
24322             }
24323             if(c == 'right'){
24324                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24325                 return;
24326             }
24327             */
24328         });
24329         
24330     },
24331   
24332     onFocus : function()
24333     {
24334         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24335         this.show();
24336     },
24337     
24338     onBlur : function()
24339     {
24340         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24341         this.hide();
24342     },
24343     
24344     show : function()
24345     {
24346         this.picker().show();
24347         this.pop.show();
24348         this.update();
24349         this.place();
24350         
24351         this.fireEvent('show', this, this.date);
24352     },
24353     
24354     hide : function()
24355     {
24356         this.picker().hide();
24357         this.pop.hide();
24358         
24359         this.fireEvent('hide', this, this.date);
24360     },
24361     
24362     setTime : function()
24363     {
24364         this.hide();
24365         this.setValue(this.time.format(this.format));
24366         
24367         this.fireEvent('select', this, this.date);
24368         
24369         
24370     },
24371     
24372     onMousedown: function(e){
24373         e.stopPropagation();
24374         e.preventDefault();
24375     },
24376     
24377     onIncrementHours: function()
24378     {
24379         Roo.log('onIncrementHours');
24380         this.time = this.time.add(Date.HOUR, 1);
24381         this.update();
24382         
24383     },
24384     
24385     onDecrementHours: function()
24386     {
24387         Roo.log('onDecrementHours');
24388         this.time = this.time.add(Date.HOUR, -1);
24389         this.update();
24390     },
24391     
24392     onIncrementMinutes: function()
24393     {
24394         Roo.log('onIncrementMinutes');
24395         this.time = this.time.add(Date.MINUTE, 1);
24396         this.update();
24397     },
24398     
24399     onDecrementMinutes: function()
24400     {
24401         Roo.log('onDecrementMinutes');
24402         this.time = this.time.add(Date.MINUTE, -1);
24403         this.update();
24404     },
24405     
24406     onTogglePeriod: function()
24407     {
24408         Roo.log('onTogglePeriod');
24409         this.time = this.time.add(Date.HOUR, 12);
24410         this.update();
24411     }
24412     
24413    
24414 });
24415  
24416
24417 Roo.apply(Roo.bootstrap.form.TimeField,  {
24418   
24419     template : {
24420         tag: 'div',
24421         cls: 'datepicker dropdown-menu',
24422         cn: [
24423             {
24424                 tag: 'div',
24425                 cls: 'datepicker-time',
24426                 cn: [
24427                 {
24428                     tag: 'table',
24429                     cls: 'table-condensed',
24430                     cn:[
24431                         {
24432                             tag: 'tbody',
24433                             cn: [
24434                                 {
24435                                     tag: 'tr',
24436                                     cn: [
24437                                     {
24438                                         tag: 'td',
24439                                         colspan: '7'
24440                                     }
24441                                     ]
24442                                 }
24443                             ]
24444                         },
24445                         {
24446                             tag: 'tfoot',
24447                             cn: [
24448                                 {
24449                                     tag: 'tr',
24450                                     cn: [
24451                                     {
24452                                         tag: 'th',
24453                                         colspan: '7',
24454                                         cls: '',
24455                                         cn: [
24456                                             {
24457                                                 tag: 'button',
24458                                                 cls: 'btn btn-info ok',
24459                                                 html: 'OK'
24460                                             }
24461                                         ]
24462                                     }
24463                     
24464                                     ]
24465                                 }
24466                             ]
24467                         }
24468                     ]
24469                 }
24470                 ]
24471             }
24472         ]
24473     }
24474 });
24475
24476  
24477
24478  /*
24479  * - LGPL
24480  *
24481  * MonthField
24482  * 
24483  */
24484
24485 /**
24486  * @class Roo.bootstrap.form.MonthField
24487  * @extends Roo.bootstrap.form.Input
24488  * Bootstrap MonthField class
24489  * 
24490  * @cfg {String} language default en
24491  * 
24492  * @constructor
24493  * Create a new MonthField
24494  * @param {Object} config The config object
24495  */
24496
24497 Roo.bootstrap.form.MonthField = function(config){
24498     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24499     
24500     this.addEvents({
24501         /**
24502          * @event show
24503          * Fires when this field show.
24504          * @param {Roo.bootstrap.form.MonthField} this
24505          * @param {Mixed} date The date value
24506          */
24507         show : true,
24508         /**
24509          * @event show
24510          * Fires when this field hide.
24511          * @param {Roo.bootstrap.form.MonthField} this
24512          * @param {Mixed} date The date value
24513          */
24514         hide : true,
24515         /**
24516          * @event select
24517          * Fires when select a date.
24518          * @param {Roo.bootstrap.form.MonthField} this
24519          * @param {String} oldvalue The old value
24520          * @param {String} newvalue The new value
24521          */
24522         select : true
24523     });
24524 };
24525
24526 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24527     
24528     onRender: function(ct, position)
24529     {
24530         
24531         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24532         
24533         this.language = this.language || 'en';
24534         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24535         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24536         
24537         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24538         this.isInline = false;
24539         this.isInput = true;
24540         this.component = this.el.select('.add-on', true).first() || false;
24541         this.component = (this.component && this.component.length === 0) ? false : this.component;
24542         this.hasInput = this.component && this.inputEL().length;
24543         
24544         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24545         
24546         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24547         
24548         this.picker().on('mousedown', this.onMousedown, this);
24549         this.picker().on('click', this.onClick, this);
24550         
24551         this.picker().addClass('datepicker-dropdown');
24552         
24553         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24554             v.setStyle('width', '189px');
24555         });
24556         
24557         this.fillMonths();
24558         
24559         this.update();
24560         
24561         if(this.isInline) {
24562             this.show();
24563         }
24564         
24565     },
24566     
24567     setValue: function(v, suppressEvent)
24568     {   
24569         var o = this.getValue();
24570         
24571         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24572         
24573         this.update();
24574
24575         if(suppressEvent !== true){
24576             this.fireEvent('select', this, o, v);
24577         }
24578         
24579     },
24580     
24581     getValue: function()
24582     {
24583         return this.value;
24584     },
24585     
24586     onClick: function(e) 
24587     {
24588         e.stopPropagation();
24589         e.preventDefault();
24590         
24591         var target = e.getTarget();
24592         
24593         if(target.nodeName.toLowerCase() === 'i'){
24594             target = Roo.get(target).dom.parentNode;
24595         }
24596         
24597         var nodeName = target.nodeName;
24598         var className = target.className;
24599         var html = target.innerHTML;
24600         
24601         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24602             return;
24603         }
24604         
24605         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24606         
24607         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24608         
24609         this.hide();
24610                         
24611     },
24612     
24613     picker : function()
24614     {
24615         return this.pickerEl;
24616     },
24617     
24618     fillMonths: function()
24619     {    
24620         var i = 0;
24621         var months = this.picker().select('>.datepicker-months td', true).first();
24622         
24623         months.dom.innerHTML = '';
24624         
24625         while (i < 12) {
24626             var month = {
24627                 tag: 'span',
24628                 cls: 'month',
24629                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24630             };
24631             
24632             months.createChild(month);
24633         }
24634         
24635     },
24636     
24637     update: function()
24638     {
24639         var _this = this;
24640         
24641         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24642             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24643         }
24644         
24645         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24646             e.removeClass('active');
24647             
24648             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24649                 e.addClass('active');
24650             }
24651         })
24652     },
24653     
24654     place: function()
24655     {
24656         if(this.isInline) {
24657             return;
24658         }
24659         
24660         this.picker().removeClass(['bottom', 'top']);
24661         
24662         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24663             /*
24664              * place to the top of element!
24665              *
24666              */
24667             
24668             this.picker().addClass('top');
24669             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24670             
24671             return;
24672         }
24673         
24674         this.picker().addClass('bottom');
24675         
24676         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24677     },
24678     
24679     onFocus : function()
24680     {
24681         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24682         this.show();
24683     },
24684     
24685     onBlur : function()
24686     {
24687         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24688         
24689         var d = this.inputEl().getValue();
24690         
24691         this.setValue(d);
24692                 
24693         this.hide();
24694     },
24695     
24696     show : function()
24697     {
24698         this.picker().show();
24699         this.picker().select('>.datepicker-months', true).first().show();
24700         this.update();
24701         this.place();
24702         
24703         this.fireEvent('show', this, this.date);
24704     },
24705     
24706     hide : function()
24707     {
24708         if(this.isInline) {
24709             return;
24710         }
24711         this.picker().hide();
24712         this.fireEvent('hide', this, this.date);
24713         
24714     },
24715     
24716     onMousedown: function(e)
24717     {
24718         e.stopPropagation();
24719         e.preventDefault();
24720     },
24721     
24722     keyup: function(e)
24723     {
24724         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24725         this.update();
24726     },
24727
24728     fireKey: function(e)
24729     {
24730         if (!this.picker().isVisible()){
24731             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24732                 this.show();
24733             }
24734             return;
24735         }
24736         
24737         var dir;
24738         
24739         switch(e.keyCode){
24740             case 27: // escape
24741                 this.hide();
24742                 e.preventDefault();
24743                 break;
24744             case 37: // left
24745             case 39: // right
24746                 dir = e.keyCode == 37 ? -1 : 1;
24747                 
24748                 this.vIndex = this.vIndex + dir;
24749                 
24750                 if(this.vIndex < 0){
24751                     this.vIndex = 0;
24752                 }
24753                 
24754                 if(this.vIndex > 11){
24755                     this.vIndex = 11;
24756                 }
24757                 
24758                 if(isNaN(this.vIndex)){
24759                     this.vIndex = 0;
24760                 }
24761                 
24762                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 
24764                 break;
24765             case 38: // up
24766             case 40: // down
24767                 
24768                 dir = e.keyCode == 38 ? -1 : 1;
24769                 
24770                 this.vIndex = this.vIndex + dir * 4;
24771                 
24772                 if(this.vIndex < 0){
24773                     this.vIndex = 0;
24774                 }
24775                 
24776                 if(this.vIndex > 11){
24777                     this.vIndex = 11;
24778                 }
24779                 
24780                 if(isNaN(this.vIndex)){
24781                     this.vIndex = 0;
24782                 }
24783                 
24784                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24785                 break;
24786                 
24787             case 13: // enter
24788                 
24789                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24790                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24791                 }
24792                 
24793                 this.hide();
24794                 e.preventDefault();
24795                 break;
24796             case 9: // tab
24797                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24798                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24799                 }
24800                 this.hide();
24801                 break;
24802             case 16: // shift
24803             case 17: // ctrl
24804             case 18: // alt
24805                 break;
24806             default :
24807                 this.hide();
24808                 
24809         }
24810     },
24811     
24812     remove: function() 
24813     {
24814         this.picker().remove();
24815     }
24816    
24817 });
24818
24819 Roo.apply(Roo.bootstrap.form.MonthField,  {
24820     
24821     content : {
24822         tag: 'tbody',
24823         cn: [
24824         {
24825             tag: 'tr',
24826             cn: [
24827             {
24828                 tag: 'td',
24829                 colspan: '7'
24830             }
24831             ]
24832         }
24833         ]
24834     },
24835     
24836     dates:{
24837         en: {
24838             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24839             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24840         }
24841     }
24842 });
24843
24844 Roo.apply(Roo.bootstrap.form.MonthField,  {
24845   
24846     template : {
24847         tag: 'div',
24848         cls: 'datepicker dropdown-menu roo-dynamic',
24849         cn: [
24850             {
24851                 tag: 'div',
24852                 cls: 'datepicker-months',
24853                 cn: [
24854                 {
24855                     tag: 'table',
24856                     cls: 'table-condensed',
24857                     cn:[
24858                         Roo.bootstrap.form.DateField.content
24859                     ]
24860                 }
24861                 ]
24862             }
24863         ]
24864     }
24865 });
24866
24867  
24868
24869  
24870  /*
24871  * - LGPL
24872  *
24873  * CheckBox
24874  * 
24875  */
24876
24877 /**
24878  * @class Roo.bootstrap.form.CheckBox
24879  * @extends Roo.bootstrap.form.Input
24880  * Bootstrap CheckBox class
24881  * 
24882  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24883  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24884  * @cfg {String} boxLabel The text that appears beside the checkbox
24885  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24886  * @cfg {Boolean} checked initnal the element
24887  * @cfg {Boolean} inline inline the element (default false)
24888  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24889  * @cfg {String} tooltip label tooltip
24890  * 
24891  * @constructor
24892  * Create a new CheckBox
24893  * @param {Object} config The config object
24894  */
24895
24896 Roo.bootstrap.form.CheckBox = function(config){
24897     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24898    
24899     this.addEvents({
24900         /**
24901         * @event check
24902         * Fires when the element is checked or unchecked.
24903         * @param {Roo.bootstrap.form.CheckBox} this This input
24904         * @param {Boolean} checked The new checked value
24905         */
24906        check : true,
24907        /**
24908         * @event click
24909         * Fires when the element is click.
24910         * @param {Roo.bootstrap.form.CheckBox} this This input
24911         */
24912        click : true
24913     });
24914     
24915 };
24916
24917 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24918   
24919     inputType: 'checkbox',
24920     inputValue: 1,
24921     valueOff: 0,
24922     boxLabel: false,
24923     checked: false,
24924     weight : false,
24925     inline: false,
24926     tooltip : '',
24927     
24928     // checkbox success does not make any sense really.. 
24929     invalidClass : "",
24930     validClass : "",
24931     
24932     
24933     getAutoCreate : function()
24934     {
24935         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24936         
24937         var id = Roo.id();
24938         
24939         var cfg = {};
24940         
24941         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24942         
24943         if(this.inline){
24944             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24945         }
24946         
24947         var input =  {
24948             tag: 'input',
24949             id : id,
24950             type : this.inputType,
24951             value : this.inputValue,
24952             cls : 'roo-' + this.inputType, //'form-box',
24953             placeholder : this.placeholder || ''
24954             
24955         };
24956         
24957         if(this.inputType != 'radio'){
24958             var hidden =  {
24959                 tag: 'input',
24960                 type : 'hidden',
24961                 cls : 'roo-hidden-value',
24962                 value : this.checked ? this.inputValue : this.valueOff
24963             };
24964         }
24965         
24966             
24967         if (this.weight) { // Validity check?
24968             cfg.cls += " " + this.inputType + "-" + this.weight;
24969         }
24970         
24971         if (this.disabled) {
24972             input.disabled=true;
24973         }
24974         
24975         if(this.checked){
24976             input.checked = this.checked;
24977         }
24978         
24979         if (this.name) {
24980             
24981             input.name = this.name;
24982             
24983             if(this.inputType != 'radio'){
24984                 hidden.name = this.name;
24985                 input.name = '_hidden_' + this.name;
24986             }
24987         }
24988         
24989         if (this.size) {
24990             input.cls += ' input-' + this.size;
24991         }
24992         
24993         var settings=this;
24994         
24995         ['xs','sm','md','lg'].map(function(size){
24996             if (settings[size]) {
24997                 cfg.cls += ' col-' + size + '-' + settings[size];
24998             }
24999         });
25000         
25001         var inputblock = input;
25002          
25003         if (this.before || this.after) {
25004             
25005             inputblock = {
25006                 cls : 'input-group',
25007                 cn :  [] 
25008             };
25009             
25010             if (this.before) {
25011                 inputblock.cn.push({
25012                     tag :'span',
25013                     cls : 'input-group-addon',
25014                     html : this.before
25015                 });
25016             }
25017             
25018             inputblock.cn.push(input);
25019             
25020             if(this.inputType != 'radio'){
25021                 inputblock.cn.push(hidden);
25022             }
25023             
25024             if (this.after) {
25025                 inputblock.cn.push({
25026                     tag :'span',
25027                     cls : 'input-group-addon',
25028                     html : this.after
25029                 });
25030             }
25031             
25032         }
25033         var boxLabelCfg = false;
25034         
25035         if(this.boxLabel){
25036            
25037             boxLabelCfg = {
25038                 tag: 'label',
25039                 //'for': id, // box label is handled by onclick - so no for...
25040                 cls: 'box-label',
25041                 html: this.boxLabel
25042             };
25043             if(this.tooltip){
25044                 boxLabelCfg.tooltip = this.tooltip;
25045             }
25046              
25047         }
25048         
25049         
25050         if (align ==='left' && this.fieldLabel.length) {
25051 //                Roo.log("left and has label");
25052             cfg.cn = [
25053                 {
25054                     tag: 'label',
25055                     'for' :  id,
25056                     cls : 'control-label',
25057                     html : this.fieldLabel
25058                 },
25059                 {
25060                     cls : "", 
25061                     cn: [
25062                         inputblock
25063                     ]
25064                 }
25065             ];
25066             
25067             if (boxLabelCfg) {
25068                 cfg.cn[1].cn.push(boxLabelCfg);
25069             }
25070             
25071             if(this.labelWidth > 12){
25072                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25073             }
25074             
25075             if(this.labelWidth < 13 && this.labelmd == 0){
25076                 this.labelmd = this.labelWidth;
25077             }
25078             
25079             if(this.labellg > 0){
25080                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25081                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25082             }
25083             
25084             if(this.labelmd > 0){
25085                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25086                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25087             }
25088             
25089             if(this.labelsm > 0){
25090                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25091                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25092             }
25093             
25094             if(this.labelxs > 0){
25095                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25096                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25097             }
25098             
25099         } else if ( this.fieldLabel.length) {
25100 //                Roo.log(" label");
25101                 cfg.cn = [
25102                    
25103                     {
25104                         tag: this.boxLabel ? 'span' : 'label',
25105                         'for': id,
25106                         cls: 'control-label box-input-label',
25107                         //cls : 'input-group-addon',
25108                         html : this.fieldLabel
25109                     },
25110                     
25111                     inputblock
25112                     
25113                 ];
25114                 if (boxLabelCfg) {
25115                     cfg.cn.push(boxLabelCfg);
25116                 }
25117
25118         } else {
25119             
25120 //                Roo.log(" no label && no align");
25121                 cfg.cn = [  inputblock ] ;
25122                 if (boxLabelCfg) {
25123                     cfg.cn.push(boxLabelCfg);
25124                 }
25125
25126                 
25127         }
25128         
25129        
25130         
25131         if(this.inputType != 'radio'){
25132             cfg.cn.push(hidden);
25133         }
25134         
25135         return cfg;
25136         
25137     },
25138     
25139     /**
25140      * return the real input element.
25141      */
25142     inputEl: function ()
25143     {
25144         return this.el.select('input.roo-' + this.inputType,true).first();
25145     },
25146     hiddenEl: function ()
25147     {
25148         return this.el.select('input.roo-hidden-value',true).first();
25149     },
25150     
25151     labelEl: function()
25152     {
25153         return this.el.select('label.control-label',true).first();
25154     },
25155     /* depricated... */
25156     
25157     label: function()
25158     {
25159         return this.labelEl();
25160     },
25161     
25162     boxLabelEl: function()
25163     {
25164         return this.el.select('label.box-label',true).first();
25165     },
25166     
25167     initEvents : function()
25168     {
25169 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25170         
25171         this.inputEl().on('click', this.onClick,  this);
25172         
25173         if (this.boxLabel) { 
25174             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25175         }
25176         
25177         this.startValue = this.getValue();
25178         
25179         if(this.groupId){
25180             Roo.bootstrap.form.CheckBox.register(this);
25181         }
25182     },
25183     
25184     onClick : function(e)
25185     {   
25186         if(this.fireEvent('click', this, e) !== false){
25187             this.setChecked(!this.checked);
25188         }
25189         
25190     },
25191     
25192     setChecked : function(state,suppressEvent)
25193     {
25194         this.startValue = this.getValue();
25195
25196         if(this.inputType == 'radio'){
25197             
25198             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25199                 e.dom.checked = false;
25200             });
25201             
25202             this.inputEl().dom.checked = true;
25203             
25204             this.inputEl().dom.value = this.inputValue;
25205             
25206             if(suppressEvent !== true){
25207                 this.fireEvent('check', this, true);
25208             }
25209             
25210             this.validate();
25211             
25212             return;
25213         }
25214         
25215         this.checked = state;
25216         
25217         this.inputEl().dom.checked = state;
25218         
25219         
25220         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25221         
25222         if(suppressEvent !== true){
25223             this.fireEvent('check', this, state);
25224         }
25225         
25226         this.validate();
25227     },
25228     
25229     getValue : function()
25230     {
25231         if(this.inputType == 'radio'){
25232             return this.getGroupValue();
25233         }
25234         
25235         return this.hiddenEl().dom.value;
25236         
25237     },
25238     
25239     getGroupValue : function()
25240     {
25241         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25242             return '';
25243         }
25244         
25245         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25246     },
25247     
25248     setValue : function(v,suppressEvent)
25249     {
25250         if(this.inputType == 'radio'){
25251             this.setGroupValue(v, suppressEvent);
25252             return;
25253         }
25254         
25255         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25256         
25257         this.validate();
25258     },
25259     
25260     setGroupValue : function(v, suppressEvent)
25261     {
25262         this.startValue = this.getValue();
25263         
25264         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25265             e.dom.checked = false;
25266             
25267             if(e.dom.value == v){
25268                 e.dom.checked = true;
25269             }
25270         });
25271         
25272         if(suppressEvent !== true){
25273             this.fireEvent('check', this, true);
25274         }
25275
25276         this.validate();
25277         
25278         return;
25279     },
25280     
25281     validate : function()
25282     {
25283         if(this.getVisibilityEl().hasClass('hidden')){
25284             return true;
25285         }
25286         
25287         if(
25288                 this.disabled || 
25289                 (this.inputType == 'radio' && this.validateRadio()) ||
25290                 (this.inputType == 'checkbox' && this.validateCheckbox())
25291         ){
25292             this.markValid();
25293             return true;
25294         }
25295         
25296         this.markInvalid();
25297         return false;
25298     },
25299     
25300     validateRadio : function()
25301     {
25302         if(this.getVisibilityEl().hasClass('hidden')){
25303             return true;
25304         }
25305         
25306         if(this.allowBlank){
25307             return true;
25308         }
25309         
25310         var valid = false;
25311         
25312         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25313             if(!e.dom.checked){
25314                 return;
25315             }
25316             
25317             valid = true;
25318             
25319             return false;
25320         });
25321         
25322         return valid;
25323     },
25324     
25325     validateCheckbox : function()
25326     {
25327         if(!this.groupId){
25328             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25329             //return (this.getValue() == this.inputValue) ? true : false;
25330         }
25331         
25332         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25333         
25334         if(!group){
25335             return false;
25336         }
25337         
25338         var r = false;
25339         
25340         for(var i in group){
25341             if(group[i].el.isVisible(true)){
25342                 r = false;
25343                 break;
25344             }
25345             
25346             r = true;
25347         }
25348         
25349         for(var i in group){
25350             if(r){
25351                 break;
25352             }
25353             
25354             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25355         }
25356         
25357         return r;
25358     },
25359     
25360     /**
25361      * Mark this field as valid
25362      */
25363     markValid : function()
25364     {
25365         var _this = this;
25366         
25367         this.fireEvent('valid', this);
25368         
25369         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25370         
25371         if(this.groupId){
25372             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25373         }
25374         
25375         if(label){
25376             label.markValid();
25377         }
25378
25379         if(this.inputType == 'radio'){
25380             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25381                 var fg = e.findParent('.form-group', false, true);
25382                 if (Roo.bootstrap.version == 3) {
25383                     fg.removeClass([_this.invalidClass, _this.validClass]);
25384                     fg.addClass(_this.validClass);
25385                 } else {
25386                     fg.removeClass(['is-valid', 'is-invalid']);
25387                     fg.addClass('is-valid');
25388                 }
25389             });
25390             
25391             return;
25392         }
25393
25394         if(!this.groupId){
25395             var fg = this.el.findParent('.form-group', false, true);
25396             if (Roo.bootstrap.version == 3) {
25397                 fg.removeClass([this.invalidClass, this.validClass]);
25398                 fg.addClass(this.validClass);
25399             } else {
25400                 fg.removeClass(['is-valid', 'is-invalid']);
25401                 fg.addClass('is-valid');
25402             }
25403             return;
25404         }
25405         
25406         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25407         
25408         if(!group){
25409             return;
25410         }
25411         
25412         for(var i in group){
25413             var fg = group[i].el.findParent('.form-group', false, true);
25414             if (Roo.bootstrap.version == 3) {
25415                 fg.removeClass([this.invalidClass, this.validClass]);
25416                 fg.addClass(this.validClass);
25417             } else {
25418                 fg.removeClass(['is-valid', 'is-invalid']);
25419                 fg.addClass('is-valid');
25420             }
25421         }
25422     },
25423     
25424      /**
25425      * Mark this field as invalid
25426      * @param {String} msg The validation message
25427      */
25428     markInvalid : function(msg)
25429     {
25430         if(this.allowBlank){
25431             return;
25432         }
25433         
25434         var _this = this;
25435         
25436         this.fireEvent('invalid', this, msg);
25437         
25438         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25439         
25440         if(this.groupId){
25441             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25442         }
25443         
25444         if(label){
25445             label.markInvalid();
25446         }
25447             
25448         if(this.inputType == 'radio'){
25449             
25450             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25451                 var fg = e.findParent('.form-group', false, true);
25452                 if (Roo.bootstrap.version == 3) {
25453                     fg.removeClass([_this.invalidClass, _this.validClass]);
25454                     fg.addClass(_this.invalidClass);
25455                 } else {
25456                     fg.removeClass(['is-invalid', 'is-valid']);
25457                     fg.addClass('is-invalid');
25458                 }
25459             });
25460             
25461             return;
25462         }
25463         
25464         if(!this.groupId){
25465             var fg = this.el.findParent('.form-group', false, true);
25466             if (Roo.bootstrap.version == 3) {
25467                 fg.removeClass([_this.invalidClass, _this.validClass]);
25468                 fg.addClass(_this.invalidClass);
25469             } else {
25470                 fg.removeClass(['is-invalid', 'is-valid']);
25471                 fg.addClass('is-invalid');
25472             }
25473             return;
25474         }
25475         
25476         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25477         
25478         if(!group){
25479             return;
25480         }
25481         
25482         for(var i in group){
25483             var fg = group[i].el.findParent('.form-group', false, true);
25484             if (Roo.bootstrap.version == 3) {
25485                 fg.removeClass([_this.invalidClass, _this.validClass]);
25486                 fg.addClass(_this.invalidClass);
25487             } else {
25488                 fg.removeClass(['is-invalid', 'is-valid']);
25489                 fg.addClass('is-invalid');
25490             }
25491         }
25492         
25493     },
25494     
25495     clearInvalid : function()
25496     {
25497         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25498         
25499         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25500         
25501         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25502         
25503         if (label && label.iconEl) {
25504             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25505             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25506         }
25507     },
25508     
25509     disable : function()
25510     {
25511         if(this.inputType != 'radio'){
25512             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25513             return;
25514         }
25515         
25516         var _this = this;
25517         
25518         if(this.rendered){
25519             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25520                 _this.getActionEl().addClass(this.disabledClass);
25521                 e.dom.disabled = true;
25522             });
25523         }
25524         
25525         this.disabled = true;
25526         this.fireEvent("disable", this);
25527         return this;
25528     },
25529
25530     enable : function()
25531     {
25532         if(this.inputType != 'radio'){
25533             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25534             return;
25535         }
25536         
25537         var _this = this;
25538         
25539         if(this.rendered){
25540             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25541                 _this.getActionEl().removeClass(this.disabledClass);
25542                 e.dom.disabled = false;
25543             });
25544         }
25545         
25546         this.disabled = false;
25547         this.fireEvent("enable", this);
25548         return this;
25549     },
25550     
25551     setBoxLabel : function(v)
25552     {
25553         this.boxLabel = v;
25554         
25555         if(this.rendered){
25556             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25557         }
25558     }
25559
25560 });
25561
25562 Roo.apply(Roo.bootstrap.form.CheckBox, {
25563     
25564     groups: {},
25565     
25566      /**
25567     * register a CheckBox Group
25568     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25569     */
25570     register : function(checkbox)
25571     {
25572         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25573             this.groups[checkbox.groupId] = {};
25574         }
25575         
25576         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25577             return;
25578         }
25579         
25580         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25581         
25582     },
25583     /**
25584     * fetch a CheckBox Group based on the group ID
25585     * @param {string} the group ID
25586     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25587     */
25588     get: function(groupId) {
25589         if (typeof(this.groups[groupId]) == 'undefined') {
25590             return false;
25591         }
25592         
25593         return this.groups[groupId] ;
25594     }
25595     
25596     
25597 });
25598 /*
25599  * - LGPL
25600  *
25601  * RadioItem
25602  * 
25603  */
25604
25605 /**
25606  * @class Roo.bootstrap.form.Radio
25607  * @extends Roo.bootstrap.Component
25608  * Bootstrap Radio class
25609  * @cfg {String} boxLabel - the label associated
25610  * @cfg {String} value - the value of radio
25611  * 
25612  * @constructor
25613  * Create a new Radio
25614  * @param {Object} config The config object
25615  */
25616 Roo.bootstrap.form.Radio = function(config){
25617     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25618     
25619 };
25620
25621 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25622     
25623     boxLabel : '',
25624     
25625     value : '',
25626     
25627     getAutoCreate : function()
25628     {
25629         var cfg = {
25630             tag : 'div',
25631             cls : 'form-group radio',
25632             cn : [
25633                 {
25634                     tag : 'label',
25635                     cls : 'box-label',
25636                     html : this.boxLabel
25637                 }
25638             ]
25639         };
25640         
25641         return cfg;
25642     },
25643     
25644     initEvents : function() 
25645     {
25646         this.parent().register(this);
25647         
25648         this.el.on('click', this.onClick, this);
25649         
25650     },
25651     
25652     onClick : function(e)
25653     {
25654         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25655             this.setChecked(true);
25656         }
25657     },
25658     
25659     setChecked : function(state, suppressEvent)
25660     {
25661         this.parent().setValue(this.value, suppressEvent);
25662         
25663     },
25664     
25665     setBoxLabel : function(v)
25666     {
25667         this.boxLabel = v;
25668         
25669         if(this.rendered){
25670             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25671         }
25672     }
25673     
25674 });
25675  
25676
25677  /*
25678  * - LGPL
25679  *
25680  * Input
25681  * 
25682  */
25683
25684 /**
25685  * @class Roo.bootstrap.form.SecurePass
25686  * @extends Roo.bootstrap.form.Input
25687  * Bootstrap SecurePass class
25688  *
25689  * 
25690  * @constructor
25691  * Create a new SecurePass
25692  * @param {Object} config The config object
25693  */
25694  
25695 Roo.bootstrap.form.SecurePass = function (config) {
25696     // these go here, so the translation tool can replace them..
25697     this.errors = {
25698         PwdEmpty: "Please type a password, and then retype it to confirm.",
25699         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25705         TooWeak: "Your password is Too Weak."
25706     },
25707     this.meterLabel = "Password strength:";
25708     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25709     this.meterClass = [
25710         "roo-password-meter-tooweak", 
25711         "roo-password-meter-weak", 
25712         "roo-password-meter-medium", 
25713         "roo-password-meter-strong", 
25714         "roo-password-meter-grey"
25715     ];
25716     
25717     this.errors = {};
25718     
25719     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25720 }
25721
25722 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25723     /**
25724      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25725      * {
25726      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25727      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25728      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25729      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25730      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25731      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25732      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25733      * })
25734      */
25735     // private
25736     
25737     meterWidth: 300,
25738     errorMsg :'',    
25739     errors: false,
25740     imageRoot: '/',
25741     /**
25742      * @cfg {String/Object} Label for the strength meter (defaults to
25743      * 'Password strength:')
25744      */
25745     // private
25746     meterLabel: '',
25747     /**
25748      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25749      * ['Weak', 'Medium', 'Strong'])
25750      */
25751     // private    
25752     pwdStrengths: false,    
25753     // private
25754     strength: 0,
25755     // private
25756     _lastPwd: null,
25757     // private
25758     kCapitalLetter: 0,
25759     kSmallLetter: 1,
25760     kDigit: 2,
25761     kPunctuation: 3,
25762     
25763     insecure: false,
25764     // private
25765     initEvents: function ()
25766     {
25767         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25768
25769         if (this.el.is('input[type=password]') && Roo.isSafari) {
25770             this.el.on('keydown', this.SafariOnKeyDown, this);
25771         }
25772
25773         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25774     },
25775     // private
25776     onRender: function (ct, position)
25777     {
25778         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25779         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25780         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25781
25782         this.trigger.createChild({
25783                    cn: [
25784                     {
25785                     //id: 'PwdMeter',
25786                     tag: 'div',
25787                     cls: 'roo-password-meter-grey col-xs-12',
25788                     style: {
25789                         //width: 0,
25790                         //width: this.meterWidth + 'px'                                                
25791                         }
25792                     },
25793                     {                            
25794                          cls: 'roo-password-meter-text'                          
25795                     }
25796                 ]            
25797         });
25798
25799          
25800         if (this.hideTrigger) {
25801             this.trigger.setDisplayed(false);
25802         }
25803         this.setSize(this.width || '', this.height || '');
25804     },
25805     // private
25806     onDestroy: function ()
25807     {
25808         if (this.trigger) {
25809             this.trigger.removeAllListeners();
25810             this.trigger.remove();
25811         }
25812         if (this.wrap) {
25813             this.wrap.remove();
25814         }
25815         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25816     },
25817     // private
25818     checkStrength: function ()
25819     {
25820         var pwd = this.inputEl().getValue();
25821         if (pwd == this._lastPwd) {
25822             return;
25823         }
25824
25825         var strength;
25826         if (this.ClientSideStrongPassword(pwd)) {
25827             strength = 3;
25828         } else if (this.ClientSideMediumPassword(pwd)) {
25829             strength = 2;
25830         } else if (this.ClientSideWeakPassword(pwd)) {
25831             strength = 1;
25832         } else {
25833             strength = 0;
25834         }
25835         
25836         Roo.log('strength1: ' + strength);
25837         
25838         //var pm = this.trigger.child('div/div/div').dom;
25839         var pm = this.trigger.child('div/div');
25840         pm.removeClass(this.meterClass);
25841         pm.addClass(this.meterClass[strength]);
25842                 
25843         
25844         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25845                 
25846         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25847         
25848         this._lastPwd = pwd;
25849     },
25850     reset: function ()
25851     {
25852         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25853         
25854         this._lastPwd = '';
25855         
25856         var pm = this.trigger.child('div/div');
25857         pm.removeClass(this.meterClass);
25858         pm.addClass('roo-password-meter-grey');        
25859         
25860         
25861         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25862         
25863         pt.innerHTML = '';
25864         this.inputEl().dom.type='password';
25865     },
25866     // private
25867     validateValue: function (value)
25868     {
25869         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25870             return false;
25871         }
25872         if (value.length == 0) {
25873             if (this.allowBlank) {
25874                 this.clearInvalid();
25875                 return true;
25876             }
25877
25878             this.markInvalid(this.errors.PwdEmpty);
25879             this.errorMsg = this.errors.PwdEmpty;
25880             return false;
25881         }
25882         
25883         if(this.insecure){
25884             return true;
25885         }
25886         
25887         if (!value.match(/[\x21-\x7e]+/)) {
25888             this.markInvalid(this.errors.PwdBadChar);
25889             this.errorMsg = this.errors.PwdBadChar;
25890             return false;
25891         }
25892         if (value.length < 6) {
25893             this.markInvalid(this.errors.PwdShort);
25894             this.errorMsg = this.errors.PwdShort;
25895             return false;
25896         }
25897         if (value.length > 16) {
25898             this.markInvalid(this.errors.PwdLong);
25899             this.errorMsg = this.errors.PwdLong;
25900             return false;
25901         }
25902         var strength;
25903         if (this.ClientSideStrongPassword(value)) {
25904             strength = 3;
25905         } else if (this.ClientSideMediumPassword(value)) {
25906             strength = 2;
25907         } else if (this.ClientSideWeakPassword(value)) {
25908             strength = 1;
25909         } else {
25910             strength = 0;
25911         }
25912
25913         
25914         if (strength < 2) {
25915             //this.markInvalid(this.errors.TooWeak);
25916             this.errorMsg = this.errors.TooWeak;
25917             //return false;
25918         }
25919         
25920         
25921         console.log('strength2: ' + strength);
25922         
25923         //var pm = this.trigger.child('div/div/div').dom;
25924         
25925         var pm = this.trigger.child('div/div');
25926         pm.removeClass(this.meterClass);
25927         pm.addClass(this.meterClass[strength]);
25928                 
25929         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25930                 
25931         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25932         
25933         this.errorMsg = ''; 
25934         return true;
25935     },
25936     // private
25937     CharacterSetChecks: function (type)
25938     {
25939         this.type = type;
25940         this.fResult = false;
25941     },
25942     // private
25943     isctype: function (character, type)
25944     {
25945         switch (type) {  
25946             case this.kCapitalLetter:
25947                 if (character >= 'A' && character <= 'Z') {
25948                     return true;
25949                 }
25950                 break;
25951             
25952             case this.kSmallLetter:
25953                 if (character >= 'a' && character <= 'z') {
25954                     return true;
25955                 }
25956                 break;
25957             
25958             case this.kDigit:
25959                 if (character >= '0' && character <= '9') {
25960                     return true;
25961                 }
25962                 break;
25963             
25964             case this.kPunctuation:
25965                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25966                     return true;
25967                 }
25968                 break;
25969             
25970             default:
25971                 return false;
25972         }
25973
25974     },
25975     // private
25976     IsLongEnough: function (pwd, size)
25977     {
25978         return !(pwd == null || isNaN(size) || pwd.length < size);
25979     },
25980     // private
25981     SpansEnoughCharacterSets: function (word, nb)
25982     {
25983         if (!this.IsLongEnough(word, nb))
25984         {
25985             return false;
25986         }
25987
25988         var characterSetChecks = new Array(
25989             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25990             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25991         );
25992         
25993         for (var index = 0; index < word.length; ++index) {
25994             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25995                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25996                     characterSetChecks[nCharSet].fResult = true;
25997                     break;
25998                 }
25999             }
26000         }
26001
26002         var nCharSets = 0;
26003         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26004             if (characterSetChecks[nCharSet].fResult) {
26005                 ++nCharSets;
26006             }
26007         }
26008
26009         if (nCharSets < nb) {
26010             return false;
26011         }
26012         return true;
26013     },
26014     // private
26015     ClientSideStrongPassword: function (pwd)
26016     {
26017         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26018     },
26019     // private
26020     ClientSideMediumPassword: function (pwd)
26021     {
26022         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26023     },
26024     // private
26025     ClientSideWeakPassword: function (pwd)
26026     {
26027         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26028     }
26029           
26030 });
26031 Roo.htmleditor = {};
26032  
26033 /**
26034  * @class Roo.htmleditor.Filter
26035  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26036  * @cfg {DomElement} node The node to iterate and filter
26037  * @cfg {boolean|String|Array} tag Tags to replace 
26038  * @constructor
26039  * Create a new Filter.
26040  * @param {Object} config Configuration options
26041  */
26042
26043
26044
26045 Roo.htmleditor.Filter = function(cfg) {
26046     Roo.apply(this.cfg);
26047     // this does not actually call walk as it's really just a abstract class
26048 }
26049
26050
26051 Roo.htmleditor.Filter.prototype = {
26052     
26053     node: false,
26054     
26055     tag: false,
26056
26057     // overrride to do replace comments.
26058     replaceComment : false,
26059     
26060     // overrride to do replace or do stuff with tags..
26061     replaceTag : false,
26062     
26063     walk : function(dom)
26064     {
26065         Roo.each( Array.from(dom.childNodes), function( e ) {
26066             switch(true) {
26067                 
26068                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26069                     this.replaceComment(e);
26070                     return;
26071                 
26072                 case e.nodeType != 1: //not a node.
26073                     return;
26074                 
26075                 case this.tag === true: // everything
26076                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26077                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26078                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26079                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26080                     if (this.replaceTag && false === this.replaceTag(e)) {
26081                         return;
26082                     }
26083                     if (e.hasChildNodes()) {
26084                         this.walk(e);
26085                     }
26086                     return;
26087                 
26088                 default:    // tags .. that do not match.
26089                     if (e.hasChildNodes()) {
26090                         this.walk(e);
26091                     }
26092             }
26093             
26094         }, this);
26095         
26096     },
26097     
26098     
26099     removeNodeKeepChildren : function( node)
26100     {
26101     
26102         ar = Array.from(node.childNodes);
26103         for (var i = 0; i < ar.length; i++) {
26104          
26105             node.removeChild(ar[i]);
26106             // what if we need to walk these???
26107             node.parentNode.insertBefore(ar[i], node);
26108            
26109         }
26110         node.parentNode.removeChild(node);
26111     }
26112 }; 
26113
26114 /**
26115  * @class Roo.htmleditor.FilterAttributes
26116  * clean attributes and  styles including http:// etc.. in attribute
26117  * @constructor
26118 * Run a new Attribute Filter
26119 * @param {Object} config Configuration options
26120  */
26121 Roo.htmleditor.FilterAttributes = function(cfg)
26122 {
26123     Roo.apply(this, cfg);
26124     this.attrib_black = this.attrib_black || [];
26125     this.attrib_white = this.attrib_white || [];
26126
26127     this.attrib_clean = this.attrib_clean || [];
26128     this.style_white = this.style_white || [];
26129     this.style_black = this.style_black || [];
26130     this.walk(cfg.node);
26131 }
26132
26133 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26134 {
26135     tag: true, // all tags
26136     
26137     attrib_black : false, // array
26138     attrib_clean : false,
26139     attrib_white : false,
26140
26141     style_white : false,
26142     style_black : false,
26143      
26144      
26145     replaceTag : function(node)
26146     {
26147         if (!node.attributes || !node.attributes.length) {
26148             return true;
26149         }
26150         
26151         for (var i = node.attributes.length-1; i > -1 ; i--) {
26152             var a = node.attributes[i];
26153             //console.log(a);
26154             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26155                 node.removeAttribute(a.name);
26156                 continue;
26157             }
26158             
26159             
26160             
26161             if (a.name.toLowerCase().substr(0,2)=='on')  {
26162                 node.removeAttribute(a.name);
26163                 continue;
26164             }
26165             
26166             
26167             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26168                 node.removeAttribute(a.name);
26169                 continue;
26170             }
26171             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26172                 this.cleanAttr(node,a.name,a.value); // fixme..
26173                 continue;
26174             }
26175             if (a.name == 'style') {
26176                 this.cleanStyle(node,a.name,a.value);
26177                 continue;
26178             }
26179             /// clean up MS crap..
26180             // tecnically this should be a list of valid class'es..
26181             
26182             
26183             if (a.name == 'class') {
26184                 if (a.value.match(/^Mso/)) {
26185                     node.removeAttribute('class');
26186                 }
26187                 
26188                 if (a.value.match(/^body$/)) {
26189                     node.removeAttribute('class');
26190                 }
26191                 continue;
26192             }
26193             
26194             
26195             // style cleanup!?
26196             // class cleanup?
26197             
26198         }
26199         return true; // clean children
26200     },
26201         
26202     cleanAttr: function(node, n,v)
26203     {
26204         
26205         if (v.match(/^\./) || v.match(/^\//)) {
26206             return;
26207         }
26208         if (v.match(/^(http|https):\/\//)
26209             || v.match(/^mailto:/) 
26210             || v.match(/^ftp:/)
26211             || v.match(/^data:/)
26212             ) {
26213             return;
26214         }
26215         if (v.match(/^#/)) {
26216             return;
26217         }
26218         if (v.match(/^\{/)) { // allow template editing.
26219             return;
26220         }
26221 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26222         node.removeAttribute(n);
26223         
26224     },
26225     cleanStyle : function(node,  n,v)
26226     {
26227         if (v.match(/expression/)) { //XSS?? should we even bother..
26228             node.removeAttribute(n);
26229             return;
26230         }
26231         
26232         var parts = v.split(/;/);
26233         var clean = [];
26234         
26235         Roo.each(parts, function(p) {
26236             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26237             if (!p.length) {
26238                 return true;
26239             }
26240             var l = p.split(':').shift().replace(/\s+/g,'');
26241             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26242             
26243             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26244                 return true;
26245             }
26246             //Roo.log()
26247             // only allow 'c whitelisted system attributes'
26248             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26249                 return true;
26250             }
26251             
26252             
26253             clean.push(p);
26254             return true;
26255         },this);
26256         if (clean.length) { 
26257             node.setAttribute(n, clean.join(';'));
26258         } else {
26259             node.removeAttribute(n);
26260         }
26261         
26262     }
26263         
26264         
26265         
26266     
26267 });/**
26268  * @class Roo.htmleditor.FilterBlack
26269  * remove blacklisted elements.
26270  * @constructor
26271  * Run a new Blacklisted Filter
26272  * @param {Object} config Configuration options
26273  */
26274
26275 Roo.htmleditor.FilterBlack = function(cfg)
26276 {
26277     Roo.apply(this, cfg);
26278     this.walk(cfg.node);
26279 }
26280
26281 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26282 {
26283     tag : true, // all elements.
26284    
26285     replaceTag : function(n)
26286     {
26287         n.parentNode.removeChild(n);
26288     }
26289 });
26290 /**
26291  * @class Roo.htmleditor.FilterComment
26292  * remove comments.
26293  * @constructor
26294 * Run a new Comments Filter
26295 * @param {Object} config Configuration options
26296  */
26297 Roo.htmleditor.FilterComment = function(cfg)
26298 {
26299     this.walk(cfg.node);
26300 }
26301
26302 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26303 {
26304   
26305     replaceComment : function(n)
26306     {
26307         n.parentNode.removeChild(n);
26308     }
26309 });/**
26310  * @class Roo.htmleditor.FilterKeepChildren
26311  * remove tags but keep children
26312  * @constructor
26313  * Run a new Keep Children Filter
26314  * @param {Object} config Configuration options
26315  */
26316
26317 Roo.htmleditor.FilterKeepChildren = function(cfg)
26318 {
26319     Roo.apply(this, cfg);
26320     if (this.tag === false) {
26321         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26322     }
26323     // hacky?
26324     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26325         this.cleanNamespace = true;
26326     }
26327         
26328     this.walk(cfg.node);
26329 }
26330
26331 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26332 {
26333     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26334   
26335     replaceTag : function(node)
26336     {
26337         // walk children...
26338         //Roo.log(node.tagName);
26339         var ar = Array.from(node.childNodes);
26340         //remove first..
26341         
26342         for (var i = 0; i < ar.length; i++) {
26343             var e = ar[i];
26344             if (e.nodeType == 1) {
26345                 if (
26346                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26347                     || // array and it matches
26348                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26349                     ||
26350                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26351                     ||
26352                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26353                 ) {
26354                     this.replaceTag(ar[i]); // child is blacklisted as well...
26355                     continue;
26356                 }
26357             }
26358         }  
26359         ar = Array.from(node.childNodes);
26360         for (var i = 0; i < ar.length; i++) {
26361          
26362             node.removeChild(ar[i]);
26363             // what if we need to walk these???
26364             node.parentNode.insertBefore(ar[i], node);
26365             if (this.tag !== false) {
26366                 this.walk(ar[i]);
26367                 
26368             }
26369         }
26370         //Roo.log("REMOVE:" + node.tagName);
26371         node.parentNode.removeChild(node);
26372         return false; // don't walk children
26373         
26374         
26375     }
26376 });/**
26377  * @class Roo.htmleditor.FilterParagraph
26378  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26379  * like on 'push' to remove the <p> tags and replace them with line breaks.
26380  * @constructor
26381  * Run a new Paragraph Filter
26382  * @param {Object} config Configuration options
26383  */
26384
26385 Roo.htmleditor.FilterParagraph = function(cfg)
26386 {
26387     // no need to apply config.
26388     this.walk(cfg.node);
26389 }
26390
26391 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26392 {
26393     
26394      
26395     tag : 'P',
26396     
26397      
26398     replaceTag : function(node)
26399     {
26400         
26401         if (node.childNodes.length == 1 &&
26402             node.childNodes[0].nodeType == 3 &&
26403             node.childNodes[0].textContent.trim().length < 1
26404             ) {
26405             // remove and replace with '<BR>';
26406             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26407             return false; // no need to walk..
26408         }
26409         var ar = Array.from(node.childNodes);
26410         for (var i = 0; i < ar.length; i++) {
26411             node.removeChild(ar[i]);
26412             // what if we need to walk these???
26413             node.parentNode.insertBefore(ar[i], node);
26414         }
26415         // now what about this?
26416         // <p> &nbsp; </p>
26417         
26418         // double BR.
26419         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26420         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26421         node.parentNode.removeChild(node);
26422         
26423         return false;
26424
26425     }
26426     
26427 });/**
26428  * @class Roo.htmleditor.FilterSpan
26429  * filter span's with no attributes out..
26430  * @constructor
26431  * Run a new Span Filter
26432  * @param {Object} config Configuration options
26433  */
26434
26435 Roo.htmleditor.FilterSpan = function(cfg)
26436 {
26437     // no need to apply config.
26438     this.walk(cfg.node);
26439 }
26440
26441 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26442 {
26443      
26444     tag : 'SPAN',
26445      
26446  
26447     replaceTag : function(node)
26448     {
26449         if (node.attributes && node.attributes.length > 0) {
26450             return true; // walk if there are any.
26451         }
26452         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26453         return false;
26454      
26455     }
26456     
26457 });/**
26458  * @class Roo.htmleditor.FilterTableWidth
26459   try and remove table width data - as that frequently messes up other stuff.
26460  * 
26461  *      was cleanTableWidths.
26462  *
26463  * Quite often pasting from word etc.. results in tables with column and widths.
26464  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26465  *
26466  * @constructor
26467  * Run a new Table Filter
26468  * @param {Object} config Configuration options
26469  */
26470
26471 Roo.htmleditor.FilterTableWidth = function(cfg)
26472 {
26473     // no need to apply config.
26474     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26475     this.walk(cfg.node);
26476 }
26477
26478 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26479 {
26480      
26481      
26482     
26483     replaceTag: function(node) {
26484         
26485         
26486       
26487         if (node.hasAttribute('width')) {
26488             node.removeAttribute('width');
26489         }
26490         
26491          
26492         if (node.hasAttribute("style")) {
26493             // pretty basic...
26494             
26495             var styles = node.getAttribute("style").split(";");
26496             var nstyle = [];
26497             Roo.each(styles, function(s) {
26498                 if (!s.match(/:/)) {
26499                     return;
26500                 }
26501                 var kv = s.split(":");
26502                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26503                     return;
26504                 }
26505                 // what ever is left... we allow.
26506                 nstyle.push(s);
26507             });
26508             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26509             if (!nstyle.length) {
26510                 node.removeAttribute('style');
26511             }
26512         }
26513         
26514         return true; // continue doing children..
26515     }
26516 });/**
26517  * @class Roo.htmleditor.FilterWord
26518  * try and clean up all the mess that Word generates.
26519  * 
26520  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26521  
26522  * @constructor
26523  * Run a new Span Filter
26524  * @param {Object} config Configuration options
26525  */
26526
26527 Roo.htmleditor.FilterWord = function(cfg)
26528 {
26529     // no need to apply config.
26530     this.replaceDocBullets(cfg.node);
26531     
26532     this.replaceAname(cfg.node);
26533     // this is disabled as the removal is done by other filters;
26534    // this.walk(cfg.node);
26535     
26536     
26537 }
26538
26539 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26540 {
26541     tag: true,
26542      
26543     
26544     /**
26545      * Clean up MS wordisms...
26546      */
26547     replaceTag : function(node)
26548     {
26549          
26550         // no idea what this does - span with text, replaceds with just text.
26551         if(
26552                 node.nodeName == 'SPAN' &&
26553                 !node.hasAttributes() &&
26554                 node.childNodes.length == 1 &&
26555                 node.firstChild.nodeName == "#text"  
26556         ) {
26557             var textNode = node.firstChild;
26558             node.removeChild(textNode);
26559             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26560                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26561             }
26562             node.parentNode.insertBefore(textNode, node);
26563             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26564                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26565             }
26566             
26567             node.parentNode.removeChild(node);
26568             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26569         }
26570         
26571    
26572         
26573         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26574             node.parentNode.removeChild(node);
26575             return false; // dont do chidlren
26576         }
26577         //Roo.log(node.tagName);
26578         // remove - but keep children..
26579         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26580             //Roo.log('-- removed');
26581             while (node.childNodes.length) {
26582                 var cn = node.childNodes[0];
26583                 node.removeChild(cn);
26584                 node.parentNode.insertBefore(cn, node);
26585                 // move node to parent - and clean it..
26586                 if (cn.nodeType == 1) {
26587                     this.replaceTag(cn);
26588                 }
26589                 
26590             }
26591             node.parentNode.removeChild(node);
26592             /// no need to iterate chidlren = it's got none..
26593             //this.iterateChildren(node, this.cleanWord);
26594             return false; // no need to iterate children.
26595         }
26596         // clean styles
26597         if (node.className.length) {
26598             
26599             var cn = node.className.split(/\W+/);
26600             var cna = [];
26601             Roo.each(cn, function(cls) {
26602                 if (cls.match(/Mso[a-zA-Z]+/)) {
26603                     return;
26604                 }
26605                 cna.push(cls);
26606             });
26607             node.className = cna.length ? cna.join(' ') : '';
26608             if (!cna.length) {
26609                 node.removeAttribute("class");
26610             }
26611         }
26612         
26613         if (node.hasAttribute("lang")) {
26614             node.removeAttribute("lang");
26615         }
26616         
26617         if (node.hasAttribute("style")) {
26618             
26619             var styles = node.getAttribute("style").split(";");
26620             var nstyle = [];
26621             Roo.each(styles, function(s) {
26622                 if (!s.match(/:/)) {
26623                     return;
26624                 }
26625                 var kv = s.split(":");
26626                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26627                     return;
26628                 }
26629                 // what ever is left... we allow.
26630                 nstyle.push(s);
26631             });
26632             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26633             if (!nstyle.length) {
26634                 node.removeAttribute('style');
26635             }
26636         }
26637         return true; // do children
26638         
26639         
26640         
26641     },
26642     
26643     styleToObject: function(node)
26644     {
26645         var styles = (node.getAttribute("style") || '').split(";");
26646         var ret = {};
26647         Roo.each(styles, function(s) {
26648             if (!s.match(/:/)) {
26649                 return;
26650             }
26651             var kv = s.split(":");
26652              
26653             // what ever is left... we allow.
26654             ret[kv[0].trim()] = kv[1];
26655         });
26656         return ret;
26657     },
26658     
26659     
26660     replaceAname : function (doc)
26661     {
26662         // replace all the a/name without..
26663         var aa = Array.from(doc.getElementsByTagName('a'));
26664         for (var i = 0; i  < aa.length; i++) {
26665             var a = aa[i];
26666             if (a.hasAttribute("name")) {
26667                 a.removeAttribute("name");
26668             }
26669             if (a.hasAttribute("href")) {
26670                 continue;
26671             }
26672             // reparent children.
26673             this.removeNodeKeepChildren(a);
26674             
26675         }
26676         
26677         
26678         
26679     },
26680
26681     
26682     
26683     replaceDocBullets : function(doc)
26684     {
26685         // this is a bit odd - but it appears some indents use ql-indent-1
26686          //Roo.log(doc.innerHTML);
26687         
26688         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26689         for( var i = 0; i < listpara.length; i ++) {
26690             listpara[i].className = "MsoListParagraph";
26691         }
26692         
26693         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26694         for( var i = 0; i < listpara.length; i ++) {
26695             listpara[i].className = "MsoListParagraph";
26696         }
26697         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26698         for( var i = 0; i < listpara.length; i ++) {
26699             listpara[i].className = "MsoListParagraph";
26700         }
26701         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26702         for( var i = 0; i < listpara.length; i ++) {
26703             listpara[i].className = "MsoListParagraph";
26704         }
26705         
26706         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26707         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26708         for( var i = 0; i < htwo.length; i ++) {
26709             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26710                 htwo[i].className = "MsoListParagraph";
26711             }
26712         }
26713         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26714         for( var i = 0; i < listpara.length; i ++) {
26715             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26716                 listpara[i].className = "MsoListParagraph";
26717             } else {
26718                 listpara[i].className = "MsoNormalx";
26719             }
26720         }
26721        
26722         listpara = doc.getElementsByClassName('MsoListParagraph');
26723         // Roo.log(doc.innerHTML);
26724         
26725         
26726         
26727         while(listpara.length) {
26728             
26729             this.replaceDocBullet(listpara.item(0));
26730         }
26731       
26732     },
26733     
26734      
26735     
26736     replaceDocBullet : function(p)
26737     {
26738         // gather all the siblings.
26739         var ns = p,
26740             parent = p.parentNode,
26741             doc = parent.ownerDocument,
26742             items = [];
26743             
26744         var listtype = 'ul';   
26745         while (ns) {
26746             if (ns.nodeType != 1) {
26747                 ns = ns.nextSibling;
26748                 continue;
26749             }
26750             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26751                 break;
26752             }
26753             var spans = ns.getElementsByTagName('span');
26754             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26755                 items.push(ns);
26756                 ns = ns.nextSibling;
26757                 has_list = true;
26758                 if (spans.length && spans[0].hasAttribute('style')) {
26759                     var  style = this.styleToObject(spans[0]);
26760                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26761                         listtype = 'ol';
26762                     }
26763                 }
26764                 
26765                 continue;
26766             }
26767             var spans = ns.getElementsByTagName('span');
26768             if (!spans.length) {
26769                 break;
26770             }
26771             var has_list  = false;
26772             for(var i = 0; i < spans.length; i++) {
26773                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26774                     has_list = true;
26775                     break;
26776                 }
26777             }
26778             if (!has_list) {
26779                 break;
26780             }
26781             items.push(ns);
26782             ns = ns.nextSibling;
26783             
26784             
26785         }
26786         if (!items.length) {
26787             ns.className = "";
26788             return;
26789         }
26790         
26791         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26792         parent.insertBefore(ul, p);
26793         var lvl = 0;
26794         var stack = [ ul ];
26795         var last_li = false;
26796         
26797         var margin_to_depth = {};
26798         max_margins = -1;
26799         
26800         items.forEach(function(n, ipos) {
26801             //Roo.log("got innertHMLT=" + n.innerHTML);
26802             
26803             var spans = n.getElementsByTagName('span');
26804             if (!spans.length) {
26805                 //Roo.log("No spans found");
26806                  
26807                 parent.removeChild(n);
26808                 
26809                 
26810                 return; // skip it...
26811             }
26812            
26813                 
26814             var num = 1;
26815             var style = {};
26816             for(var i = 0; i < spans.length; i++) {
26817             
26818                 style = this.styleToObject(spans[i]);
26819                 if (typeof(style['mso-list']) == 'undefined') {
26820                     continue;
26821                 }
26822                 if (listtype == 'ol') {
26823                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26824                 }
26825                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26826                 break;
26827             }
26828             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26829             style = this.styleToObject(n); // mo-list is from the parent node.
26830             if (typeof(style['mso-list']) == 'undefined') {
26831                 //Roo.log("parent is missing level");
26832                   
26833                 parent.removeChild(n);
26834                  
26835                 return;
26836             }
26837             
26838             var margin = style['margin-left'];
26839             if (typeof(margin_to_depth[margin]) == 'undefined') {
26840                 max_margins++;
26841                 margin_to_depth[margin] = max_margins;
26842             }
26843             nlvl = margin_to_depth[margin] ;
26844              
26845             if (nlvl > lvl) {
26846                 //new indent
26847                 var nul = doc.createElement(listtype); // what about number lists...
26848                 if (!last_li) {
26849                     last_li = doc.createElement('li');
26850                     stack[lvl].appendChild(last_li);
26851                 }
26852                 last_li.appendChild(nul);
26853                 stack[nlvl] = nul;
26854                 
26855             }
26856             lvl = nlvl;
26857             
26858             // not starting at 1..
26859             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26860                 stack[nlvl].setAttribute("start", num);
26861             }
26862             
26863             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26864             last_li = nli;
26865             nli.innerHTML = n.innerHTML;
26866             //Roo.log("innerHTML = " + n.innerHTML);
26867             parent.removeChild(n);
26868             
26869              
26870              
26871             
26872         },this);
26873         
26874         
26875         
26876         
26877     }
26878     
26879     
26880     
26881 });
26882 /**
26883  * @class Roo.htmleditor.FilterStyleToTag
26884  * part of the word stuff... - certain 'styles' should be converted to tags.
26885  * eg.
26886  *   font-weight: bold -> bold
26887  *   ?? super / subscrit etc..
26888  * 
26889  * @constructor
26890 * Run a new style to tag filter.
26891 * @param {Object} config Configuration options
26892  */
26893 Roo.htmleditor.FilterStyleToTag = function(cfg)
26894 {
26895     
26896     this.tags = {
26897         B  : [ 'fontWeight' , 'bold'],
26898         I :  [ 'fontStyle' , 'italic'],
26899         //pre :  [ 'font-style' , 'italic'],
26900         // h1.. h6 ?? font-size?
26901         SUP : [ 'verticalAlign' , 'super' ],
26902         SUB : [ 'verticalAlign' , 'sub' ]
26903         
26904         
26905     };
26906     
26907     Roo.apply(this, cfg);
26908      
26909     
26910     this.walk(cfg.node);
26911     
26912     
26913     
26914 }
26915
26916
26917 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26918 {
26919     tag: true, // all tags
26920     
26921     tags : false,
26922     
26923     
26924     replaceTag : function(node)
26925     {
26926         
26927         
26928         if (node.getAttribute("style") === null) {
26929             return true;
26930         }
26931         var inject = [];
26932         for (var k in this.tags) {
26933             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26934                 inject.push(k);
26935                 node.style.removeProperty(this.tags[k][0]);
26936             }
26937         }
26938         if (!inject.length) {
26939             return true; 
26940         }
26941         var cn = Array.from(node.childNodes);
26942         var nn = node;
26943         Roo.each(inject, function(t) {
26944             var nc = node.ownerDocument.createElement(t);
26945             nn.appendChild(nc);
26946             nn = nc;
26947         });
26948         for(var i = 0;i < cn.length;cn++) {
26949             node.removeChild(cn[i]);
26950             nn.appendChild(cn[i]);
26951         }
26952         return true /// iterate thru
26953     }
26954     
26955 })/**
26956  * @class Roo.htmleditor.FilterLongBr
26957  * BR/BR/BR - keep a maximum of 2...
26958  * @constructor
26959  * Run a new Long BR Filter
26960  * @param {Object} config Configuration options
26961  */
26962
26963 Roo.htmleditor.FilterLongBr = function(cfg)
26964 {
26965     // no need to apply config.
26966     this.walk(cfg.node);
26967 }
26968
26969 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26970 {
26971     
26972      
26973     tag : 'BR',
26974     
26975      
26976     replaceTag : function(node)
26977     {
26978         
26979         var ps = node.nextSibling;
26980         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26981             ps = ps.nextSibling;
26982         }
26983         
26984         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26985             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26986             return false;
26987         }
26988         
26989         if (!ps || ps.nodeType != 1) {
26990             return false;
26991         }
26992         
26993         if (!ps || ps.tagName != 'BR') {
26994            
26995             return false;
26996         }
26997         
26998         
26999         
27000         
27001         
27002         if (!node.previousSibling) {
27003             return false;
27004         }
27005         var ps = node.previousSibling;
27006         
27007         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27008             ps = ps.previousSibling;
27009         }
27010         if (!ps || ps.nodeType != 1) {
27011             return false;
27012         }
27013         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27014         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27015             return false;
27016         }
27017         
27018         node.parentNode.removeChild(node); // remove me...
27019         
27020         return false; // no need to do children
27021
27022     }
27023     
27024 }); 
27025
27026 /**
27027  * @class Roo.htmleditor.FilterBlock
27028  * removes id / data-block and contenteditable that are associated with blocks
27029  * usage should be done on a cloned copy of the dom
27030  * @constructor
27031 * Run a new Attribute Filter { node : xxxx }}
27032 * @param {Object} config Configuration options
27033  */
27034 Roo.htmleditor.FilterBlock = function(cfg)
27035 {
27036     Roo.apply(this, cfg);
27037     var qa = cfg.node.querySelectorAll;
27038     this.removeAttributes('data-block');
27039     this.removeAttributes('contenteditable');
27040     this.removeAttributes('id');
27041     
27042 }
27043
27044 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27045 {
27046     node: true, // all tags
27047      
27048      
27049     removeAttributes : function(attr)
27050     {
27051         var ar = this.node.querySelectorAll('*[' + attr + ']');
27052         for (var i =0;i<ar.length;i++) {
27053             ar[i].removeAttribute(attr);
27054         }
27055     }
27056         
27057         
27058         
27059     
27060 });
27061 /***
27062  * This is based loosely on tinymce 
27063  * @class Roo.htmleditor.TidySerializer
27064  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27065  * @constructor
27066  * @method Serializer
27067  * @param {Object} settings Name/value settings object.
27068  */
27069
27070
27071 Roo.htmleditor.TidySerializer = function(settings)
27072 {
27073     Roo.apply(this, settings);
27074     
27075     this.writer = new Roo.htmleditor.TidyWriter(settings);
27076     
27077     
27078
27079 };
27080 Roo.htmleditor.TidySerializer.prototype = {
27081     
27082     /**
27083      * @param {boolean} inner do the inner of the node.
27084      */
27085     inner : false,
27086     
27087     writer : false,
27088     
27089     /**
27090     * Serializes the specified node into a string.
27091     *
27092     * @example
27093     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27094     * @method serialize
27095     * @param {DomElement} node Node instance to serialize.
27096     * @return {String} String with HTML based on DOM tree.
27097     */
27098     serialize : function(node) {
27099         
27100         // = settings.validate;
27101         var writer = this.writer;
27102         var self  = this;
27103         this.handlers = {
27104             // #text
27105             3: function(node) {
27106                 
27107                 writer.text(node.nodeValue, node);
27108             },
27109             // #comment
27110             8: function(node) {
27111                 writer.comment(node.nodeValue);
27112             },
27113             // Processing instruction
27114             7: function(node) {
27115                 writer.pi(node.name, node.nodeValue);
27116             },
27117             // Doctype
27118             10: function(node) {
27119                 writer.doctype(node.nodeValue);
27120             },
27121             // CDATA
27122             4: function(node) {
27123                 writer.cdata(node.nodeValue);
27124             },
27125             // Document fragment
27126             11: function(node) {
27127                 node = node.firstChild;
27128                 if (!node) {
27129                     return;
27130                 }
27131                 while(node) {
27132                     self.walk(node);
27133                     node = node.nextSibling
27134                 }
27135             }
27136         };
27137         writer.reset();
27138         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27139         return writer.getContent();
27140     },
27141
27142     walk: function(node)
27143     {
27144         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27145             handler = this.handlers[node.nodeType];
27146             
27147         if (handler) {
27148             handler(node);
27149             return;
27150         }
27151     
27152         var name = node.nodeName;
27153         var isEmpty = node.childNodes.length < 1;
27154       
27155         var writer = this.writer;
27156         var attrs = node.attributes;
27157         // Sort attributes
27158         
27159         writer.start(node.nodeName, attrs, isEmpty, node);
27160         if (isEmpty) {
27161             return;
27162         }
27163         node = node.firstChild;
27164         if (!node) {
27165             writer.end(name);
27166             return;
27167         }
27168         while (node) {
27169             this.walk(node);
27170             node = node.nextSibling;
27171         }
27172         writer.end(name);
27173         
27174     
27175     }
27176     // Serialize element and treat all non elements as fragments
27177    
27178 }; 
27179
27180 /***
27181  * This is based loosely on tinymce 
27182  * @class Roo.htmleditor.TidyWriter
27183  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27184  *
27185  * Known issues?
27186  * - not tested much with 'PRE' formated elements.
27187  * 
27188  *
27189  *
27190  */
27191
27192 Roo.htmleditor.TidyWriter = function(settings)
27193 {
27194     
27195     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27196     Roo.apply(this, settings);
27197     this.html = [];
27198     this.state = [];
27199      
27200     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27201   
27202 }
27203 Roo.htmleditor.TidyWriter.prototype = {
27204
27205  
27206     state : false,
27207     
27208     indent :  '  ',
27209     
27210     // part of state...
27211     indentstr : '',
27212     in_pre: false,
27213     in_inline : false,
27214     last_inline : false,
27215     encode : false,
27216      
27217     
27218             /**
27219     * Writes the a start element such as <p id="a">.
27220     *
27221     * @method start
27222     * @param {String} name Name of the element.
27223     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27224     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27225     */
27226     start: function(name, attrs, empty, node)
27227     {
27228         var i, l, attr, value;
27229         
27230         // there are some situations where adding line break && indentation will not work. will not work.
27231         // <span / b / i ... formating?
27232         
27233         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27234         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27235         
27236         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27237         
27238         var add_lb = name == 'BR' ? false : in_inline;
27239         
27240         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27241             i_inline = false;
27242         }
27243
27244         var indentstr =  this.indentstr;
27245         
27246         // e_inline = elements that can be inline, but still allow \n before and after?
27247         // only 'BR' ??? any others?
27248         
27249         // ADD LINE BEFORE tage
27250         if (!this.in_pre) {
27251             if (in_inline) {
27252                 //code
27253                 if (name == 'BR') {
27254                     this.addLine();
27255                 } else if (this.lastElementEndsWS()) {
27256                     this.addLine();
27257                 } else{
27258                     // otherwise - no new line. (and dont indent.)
27259                     indentstr = '';
27260                 }
27261                 
27262             } else {
27263                 this.addLine();
27264             }
27265         } else {
27266             indentstr = '';
27267         }
27268         
27269         this.html.push(indentstr + '<', name.toLowerCase());
27270         
27271         if (attrs) {
27272             for (i = 0, l = attrs.length; i < l; i++) {
27273                 attr = attrs[i];
27274                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27275             }
27276         }
27277      
27278         if (empty) {
27279             if (is_short) {
27280                 this.html[this.html.length] = '/>';
27281             } else {
27282                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27283             }
27284             var e_inline = name == 'BR' ? false : this.in_inline;
27285             
27286             if (!e_inline && !this.in_pre) {
27287                 this.addLine();
27288             }
27289             return;
27290         
27291         }
27292         // not empty..
27293         this.html[this.html.length] = '>';
27294         
27295         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27296         /*
27297         if (!in_inline && !in_pre) {
27298             var cn = node.firstChild;
27299             while(cn) {
27300                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27301                     in_inline = true
27302                     break;
27303                 }
27304                 cn = cn.nextSibling;
27305             }
27306              
27307         }
27308         */
27309         
27310         
27311         this.pushState({
27312             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27313             in_pre : in_pre,
27314             in_inline :  in_inline
27315         });
27316         // add a line after if we are not in a
27317         
27318         if (!in_inline && !in_pre) {
27319             this.addLine();
27320         }
27321         
27322             
27323          
27324         
27325     },
27326     
27327     lastElementEndsWS : function()
27328     {
27329         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27330         if (value === false) {
27331             return true;
27332         }
27333         return value.match(/\s+$/);
27334         
27335     },
27336     
27337     /**
27338      * Writes the a end element such as </p>.
27339      *
27340      * @method end
27341      * @param {String} name Name of the element.
27342      */
27343     end: function(name) {
27344         var value;
27345         this.popState();
27346         var indentstr = '';
27347         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27348         
27349         if (!this.in_pre && !in_inline) {
27350             this.addLine();
27351             indentstr  = this.indentstr;
27352         }
27353         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27354         this.last_inline = in_inline;
27355         
27356         // pop the indent state..
27357     },
27358     /**
27359      * Writes a text node.
27360      *
27361      * In pre - we should not mess with the contents.
27362      * 
27363      *
27364      * @method text
27365      * @param {String} text String to write out.
27366      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27367      */
27368     text: function(in_text, node)
27369     {
27370         // if not in whitespace critical
27371         if (in_text.length < 1) {
27372             return;
27373         }
27374         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27375         
27376         if (this.in_pre) {
27377             this.html[this.html.length] =  text;
27378             return;   
27379         }
27380         
27381         if (this.in_inline) {
27382             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27383             if (text != ' ') {
27384                 text = text.replace(/\s+/,' ');  // all white space to single white space
27385                 
27386                     
27387                 // if next tag is '<BR>', then we can trim right..
27388                 if (node.nextSibling &&
27389                     node.nextSibling.nodeType == 1 &&
27390                     node.nextSibling.nodeName == 'BR' )
27391                 {
27392                     text = text.replace(/\s+$/g,'');
27393                 }
27394                 // if previous tag was a BR, we can also trim..
27395                 if (node.previousSibling &&
27396                     node.previousSibling.nodeType == 1 &&
27397                     node.previousSibling.nodeName == 'BR' )
27398                 {
27399                     text = this.indentstr +  text.replace(/^\s+/g,'');
27400                 }
27401                 if (text.match(/\n/)) {
27402                     text = text.replace(
27403                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27404                     );
27405                     // remoeve the last whitespace / line break.
27406                     text = text.replace(/\n\s+$/,'');
27407                 }
27408                 // repace long lines
27409                 
27410             }
27411              
27412             this.html[this.html.length] =  text;
27413             return;   
27414         }
27415         // see if previous element was a inline element.
27416         var indentstr = this.indentstr;
27417    
27418         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27419         
27420         // should trim left?
27421         if (node.previousSibling &&
27422             node.previousSibling.nodeType == 1 &&
27423             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27424         {
27425             indentstr = '';
27426             
27427         } else {
27428             this.addLine();
27429             text = text.replace(/^\s+/,''); // trim left
27430           
27431         }
27432         // should trim right?
27433         if (node.nextSibling &&
27434             node.nextSibling.nodeType == 1 &&
27435             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27436         {
27437           // noop
27438             
27439         }  else {
27440             text = text.replace(/\s+$/,''); // trim right
27441         }
27442          
27443               
27444         
27445         
27446         
27447         if (text.length < 1) {
27448             return;
27449         }
27450         if (!text.match(/\n/)) {
27451             this.html.push(indentstr + text);
27452             return;
27453         }
27454         
27455         text = this.indentstr + text.replace(
27456             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27457         );
27458         // remoeve the last whitespace / line break.
27459         text = text.replace(/\s+$/,''); 
27460         
27461         this.html.push(text);
27462         
27463         // split and indent..
27464         
27465         
27466     },
27467     /**
27468      * Writes a cdata node such as <![CDATA[data]]>.
27469      *
27470      * @method cdata
27471      * @param {String} text String to write out inside the cdata.
27472      */
27473     cdata: function(text) {
27474         this.html.push('<![CDATA[', text, ']]>');
27475     },
27476     /**
27477     * Writes a comment node such as <!-- Comment -->.
27478     *
27479     * @method cdata
27480     * @param {String} text String to write out inside the comment.
27481     */
27482    comment: function(text) {
27483        this.html.push('<!--', text, '-->');
27484    },
27485     /**
27486      * Writes a PI node such as <?xml attr="value" ?>.
27487      *
27488      * @method pi
27489      * @param {String} name Name of the pi.
27490      * @param {String} text String to write out inside the pi.
27491      */
27492     pi: function(name, text) {
27493         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27494         this.indent != '' && this.html.push('\n');
27495     },
27496     /**
27497      * Writes a doctype node such as <!DOCTYPE data>.
27498      *
27499      * @method doctype
27500      * @param {String} text String to write out inside the doctype.
27501      */
27502     doctype: function(text) {
27503         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27504     },
27505     /**
27506      * Resets the internal buffer if one wants to reuse the writer.
27507      *
27508      * @method reset
27509      */
27510     reset: function() {
27511         this.html.length = 0;
27512         this.state = [];
27513         this.pushState({
27514             indentstr : '',
27515             in_pre : false, 
27516             in_inline : false
27517         })
27518     },
27519     /**
27520      * Returns the contents that got serialized.
27521      *
27522      * @method getContent
27523      * @return {String} HTML contents that got written down.
27524      */
27525     getContent: function() {
27526         return this.html.join('').replace(/\n$/, '');
27527     },
27528     
27529     pushState : function(cfg)
27530     {
27531         this.state.push(cfg);
27532         Roo.apply(this, cfg);
27533     },
27534     
27535     popState : function()
27536     {
27537         if (this.state.length < 1) {
27538             return; // nothing to push
27539         }
27540         var cfg = {
27541             in_pre: false,
27542             indentstr : ''
27543         };
27544         this.state.pop();
27545         if (this.state.length > 0) {
27546             cfg = this.state[this.state.length-1]; 
27547         }
27548         Roo.apply(this, cfg);
27549     },
27550     
27551     addLine: function()
27552     {
27553         if (this.html.length < 1) {
27554             return;
27555         }
27556         
27557         
27558         var value = this.html[this.html.length - 1];
27559         if (value.length > 0 && '\n' !== value) {
27560             this.html.push('\n');
27561         }
27562     }
27563     
27564     
27565 //'pre script noscript style textarea video audio iframe object code'
27566 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27567 // inline 
27568 };
27569
27570 Roo.htmleditor.TidyWriter.inline_elements = [
27571         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27572         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27573 ];
27574 Roo.htmleditor.TidyWriter.shortend_elements = [
27575     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27576     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27577 ];
27578
27579 Roo.htmleditor.TidyWriter.whitespace_elements = [
27580     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27581 ];/***
27582  * This is based loosely on tinymce 
27583  * @class Roo.htmleditor.TidyEntities
27584  * @static
27585  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27586  *
27587  * Not 100% sure this is actually used or needed.
27588  */
27589
27590 Roo.htmleditor.TidyEntities = {
27591     
27592     /**
27593      * initialize data..
27594      */
27595     init : function (){
27596      
27597         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27598        
27599     },
27600
27601
27602     buildEntitiesLookup: function(items, radix) {
27603         var i, chr, entity, lookup = {};
27604         if (!items) {
27605             return {};
27606         }
27607         items = typeof(items) == 'string' ? items.split(',') : items;
27608         radix = radix || 10;
27609         // Build entities lookup table
27610         for (i = 0; i < items.length; i += 2) {
27611             chr = String.fromCharCode(parseInt(items[i], radix));
27612             // Only add non base entities
27613             if (!this.baseEntities[chr]) {
27614                 entity = '&' + items[i + 1] + ';';
27615                 lookup[chr] = entity;
27616                 lookup[entity] = chr;
27617             }
27618         }
27619         return lookup;
27620         
27621     },
27622     
27623     asciiMap : {
27624             128: '€',
27625             130: '‚',
27626             131: 'ƒ',
27627             132: '„',
27628             133: '…',
27629             134: '†',
27630             135: '‡',
27631             136: 'ˆ',
27632             137: '‰',
27633             138: 'Š',
27634             139: '‹',
27635             140: 'Œ',
27636             142: 'Ž',
27637             145: '‘',
27638             146: '’',
27639             147: '“',
27640             148: '”',
27641             149: '•',
27642             150: '–',
27643             151: '—',
27644             152: '˜',
27645             153: '™',
27646             154: 'š',
27647             155: '›',
27648             156: 'œ',
27649             158: 'ž',
27650             159: 'Ÿ'
27651     },
27652     // Raw entities
27653     baseEntities : {
27654         '"': '&quot;',
27655         // Needs to be escaped since the YUI compressor would otherwise break the code
27656         '\'': '&#39;',
27657         '<': '&lt;',
27658         '>': '&gt;',
27659         '&': '&amp;',
27660         '`': '&#96;'
27661     },
27662     // Reverse lookup table for raw entities
27663     reverseEntities : {
27664         '&lt;': '<',
27665         '&gt;': '>',
27666         '&amp;': '&',
27667         '&quot;': '"',
27668         '&apos;': '\''
27669     },
27670     
27671     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27672     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27673     rawCharsRegExp : /[<>&\"\']/g,
27674     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27675     namedEntities  : false,
27676     namedEntitiesData : [ 
27677         '50',
27678         'nbsp',
27679         '51',
27680         'iexcl',
27681         '52',
27682         'cent',
27683         '53',
27684         'pound',
27685         '54',
27686         'curren',
27687         '55',
27688         'yen',
27689         '56',
27690         'brvbar',
27691         '57',
27692         'sect',
27693         '58',
27694         'uml',
27695         '59',
27696         'copy',
27697         '5a',
27698         'ordf',
27699         '5b',
27700         'laquo',
27701         '5c',
27702         'not',
27703         '5d',
27704         'shy',
27705         '5e',
27706         'reg',
27707         '5f',
27708         'macr',
27709         '5g',
27710         'deg',
27711         '5h',
27712         'plusmn',
27713         '5i',
27714         'sup2',
27715         '5j',
27716         'sup3',
27717         '5k',
27718         'acute',
27719         '5l',
27720         'micro',
27721         '5m',
27722         'para',
27723         '5n',
27724         'middot',
27725         '5o',
27726         'cedil',
27727         '5p',
27728         'sup1',
27729         '5q',
27730         'ordm',
27731         '5r',
27732         'raquo',
27733         '5s',
27734         'frac14',
27735         '5t',
27736         'frac12',
27737         '5u',
27738         'frac34',
27739         '5v',
27740         'iquest',
27741         '60',
27742         'Agrave',
27743         '61',
27744         'Aacute',
27745         '62',
27746         'Acirc',
27747         '63',
27748         'Atilde',
27749         '64',
27750         'Auml',
27751         '65',
27752         'Aring',
27753         '66',
27754         'AElig',
27755         '67',
27756         'Ccedil',
27757         '68',
27758         'Egrave',
27759         '69',
27760         'Eacute',
27761         '6a',
27762         'Ecirc',
27763         '6b',
27764         'Euml',
27765         '6c',
27766         'Igrave',
27767         '6d',
27768         'Iacute',
27769         '6e',
27770         'Icirc',
27771         '6f',
27772         'Iuml',
27773         '6g',
27774         'ETH',
27775         '6h',
27776         'Ntilde',
27777         '6i',
27778         'Ograve',
27779         '6j',
27780         'Oacute',
27781         '6k',
27782         'Ocirc',
27783         '6l',
27784         'Otilde',
27785         '6m',
27786         'Ouml',
27787         '6n',
27788         'times',
27789         '6o',
27790         'Oslash',
27791         '6p',
27792         'Ugrave',
27793         '6q',
27794         'Uacute',
27795         '6r',
27796         'Ucirc',
27797         '6s',
27798         'Uuml',
27799         '6t',
27800         'Yacute',
27801         '6u',
27802         'THORN',
27803         '6v',
27804         'szlig',
27805         '70',
27806         'agrave',
27807         '71',
27808         'aacute',
27809         '72',
27810         'acirc',
27811         '73',
27812         'atilde',
27813         '74',
27814         'auml',
27815         '75',
27816         'aring',
27817         '76',
27818         'aelig',
27819         '77',
27820         'ccedil',
27821         '78',
27822         'egrave',
27823         '79',
27824         'eacute',
27825         '7a',
27826         'ecirc',
27827         '7b',
27828         'euml',
27829         '7c',
27830         'igrave',
27831         '7d',
27832         'iacute',
27833         '7e',
27834         'icirc',
27835         '7f',
27836         'iuml',
27837         '7g',
27838         'eth',
27839         '7h',
27840         'ntilde',
27841         '7i',
27842         'ograve',
27843         '7j',
27844         'oacute',
27845         '7k',
27846         'ocirc',
27847         '7l',
27848         'otilde',
27849         '7m',
27850         'ouml',
27851         '7n',
27852         'divide',
27853         '7o',
27854         'oslash',
27855         '7p',
27856         'ugrave',
27857         '7q',
27858         'uacute',
27859         '7r',
27860         'ucirc',
27861         '7s',
27862         'uuml',
27863         '7t',
27864         'yacute',
27865         '7u',
27866         'thorn',
27867         '7v',
27868         'yuml',
27869         'ci',
27870         'fnof',
27871         'sh',
27872         'Alpha',
27873         'si',
27874         'Beta',
27875         'sj',
27876         'Gamma',
27877         'sk',
27878         'Delta',
27879         'sl',
27880         'Epsilon',
27881         'sm',
27882         'Zeta',
27883         'sn',
27884         'Eta',
27885         'so',
27886         'Theta',
27887         'sp',
27888         'Iota',
27889         'sq',
27890         'Kappa',
27891         'sr',
27892         'Lambda',
27893         'ss',
27894         'Mu',
27895         'st',
27896         'Nu',
27897         'su',
27898         'Xi',
27899         'sv',
27900         'Omicron',
27901         't0',
27902         'Pi',
27903         't1',
27904         'Rho',
27905         't3',
27906         'Sigma',
27907         't4',
27908         'Tau',
27909         't5',
27910         'Upsilon',
27911         't6',
27912         'Phi',
27913         't7',
27914         'Chi',
27915         't8',
27916         'Psi',
27917         't9',
27918         'Omega',
27919         'th',
27920         'alpha',
27921         'ti',
27922         'beta',
27923         'tj',
27924         'gamma',
27925         'tk',
27926         'delta',
27927         'tl',
27928         'epsilon',
27929         'tm',
27930         'zeta',
27931         'tn',
27932         'eta',
27933         'to',
27934         'theta',
27935         'tp',
27936         'iota',
27937         'tq',
27938         'kappa',
27939         'tr',
27940         'lambda',
27941         'ts',
27942         'mu',
27943         'tt',
27944         'nu',
27945         'tu',
27946         'xi',
27947         'tv',
27948         'omicron',
27949         'u0',
27950         'pi',
27951         'u1',
27952         'rho',
27953         'u2',
27954         'sigmaf',
27955         'u3',
27956         'sigma',
27957         'u4',
27958         'tau',
27959         'u5',
27960         'upsilon',
27961         'u6',
27962         'phi',
27963         'u7',
27964         'chi',
27965         'u8',
27966         'psi',
27967         'u9',
27968         'omega',
27969         'uh',
27970         'thetasym',
27971         'ui',
27972         'upsih',
27973         'um',
27974         'piv',
27975         '812',
27976         'bull',
27977         '816',
27978         'hellip',
27979         '81i',
27980         'prime',
27981         '81j',
27982         'Prime',
27983         '81u',
27984         'oline',
27985         '824',
27986         'frasl',
27987         '88o',
27988         'weierp',
27989         '88h',
27990         'image',
27991         '88s',
27992         'real',
27993         '892',
27994         'trade',
27995         '89l',
27996         'alefsym',
27997         '8cg',
27998         'larr',
27999         '8ch',
28000         'uarr',
28001         '8ci',
28002         'rarr',
28003         '8cj',
28004         'darr',
28005         '8ck',
28006         'harr',
28007         '8dl',
28008         'crarr',
28009         '8eg',
28010         'lArr',
28011         '8eh',
28012         'uArr',
28013         '8ei',
28014         'rArr',
28015         '8ej',
28016         'dArr',
28017         '8ek',
28018         'hArr',
28019         '8g0',
28020         'forall',
28021         '8g2',
28022         'part',
28023         '8g3',
28024         'exist',
28025         '8g5',
28026         'empty',
28027         '8g7',
28028         'nabla',
28029         '8g8',
28030         'isin',
28031         '8g9',
28032         'notin',
28033         '8gb',
28034         'ni',
28035         '8gf',
28036         'prod',
28037         '8gh',
28038         'sum',
28039         '8gi',
28040         'minus',
28041         '8gn',
28042         'lowast',
28043         '8gq',
28044         'radic',
28045         '8gt',
28046         'prop',
28047         '8gu',
28048         'infin',
28049         '8h0',
28050         'ang',
28051         '8h7',
28052         'and',
28053         '8h8',
28054         'or',
28055         '8h9',
28056         'cap',
28057         '8ha',
28058         'cup',
28059         '8hb',
28060         'int',
28061         '8hk',
28062         'there4',
28063         '8hs',
28064         'sim',
28065         '8i5',
28066         'cong',
28067         '8i8',
28068         'asymp',
28069         '8j0',
28070         'ne',
28071         '8j1',
28072         'equiv',
28073         '8j4',
28074         'le',
28075         '8j5',
28076         'ge',
28077         '8k2',
28078         'sub',
28079         '8k3',
28080         'sup',
28081         '8k4',
28082         'nsub',
28083         '8k6',
28084         'sube',
28085         '8k7',
28086         'supe',
28087         '8kl',
28088         'oplus',
28089         '8kn',
28090         'otimes',
28091         '8l5',
28092         'perp',
28093         '8m5',
28094         'sdot',
28095         '8o8',
28096         'lceil',
28097         '8o9',
28098         'rceil',
28099         '8oa',
28100         'lfloor',
28101         '8ob',
28102         'rfloor',
28103         '8p9',
28104         'lang',
28105         '8pa',
28106         'rang',
28107         '9ea',
28108         'loz',
28109         '9j0',
28110         'spades',
28111         '9j3',
28112         'clubs',
28113         '9j5',
28114         'hearts',
28115         '9j6',
28116         'diams',
28117         'ai',
28118         'OElig',
28119         'aj',
28120         'oelig',
28121         'b0',
28122         'Scaron',
28123         'b1',
28124         'scaron',
28125         'bo',
28126         'Yuml',
28127         'm6',
28128         'circ',
28129         'ms',
28130         'tilde',
28131         '802',
28132         'ensp',
28133         '803',
28134         'emsp',
28135         '809',
28136         'thinsp',
28137         '80c',
28138         'zwnj',
28139         '80d',
28140         'zwj',
28141         '80e',
28142         'lrm',
28143         '80f',
28144         'rlm',
28145         '80j',
28146         'ndash',
28147         '80k',
28148         'mdash',
28149         '80o',
28150         'lsquo',
28151         '80p',
28152         'rsquo',
28153         '80q',
28154         'sbquo',
28155         '80s',
28156         'ldquo',
28157         '80t',
28158         'rdquo',
28159         '80u',
28160         'bdquo',
28161         '810',
28162         'dagger',
28163         '811',
28164         'Dagger',
28165         '81g',
28166         'permil',
28167         '81p',
28168         'lsaquo',
28169         '81q',
28170         'rsaquo',
28171         '85c',
28172         'euro'
28173     ],
28174
28175          
28176     /**
28177      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28178      *
28179      * @method encodeRaw
28180      * @param {String} text Text to encode.
28181      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28182      * @return {String} Entity encoded text.
28183      */
28184     encodeRaw: function(text, attr)
28185     {
28186         var t = this;
28187         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28188             return t.baseEntities[chr] || chr;
28189         });
28190     },
28191     /**
28192      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28193      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28194      * and is exposed as the DOMUtils.encode function.
28195      *
28196      * @method encodeAllRaw
28197      * @param {String} text Text to encode.
28198      * @return {String} Entity encoded text.
28199      */
28200     encodeAllRaw: function(text) {
28201         var t = this;
28202         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28203             return t.baseEntities[chr] || chr;
28204         });
28205     },
28206     /**
28207      * Encodes the specified string using numeric entities. The core entities will be
28208      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28209      *
28210      * @method encodeNumeric
28211      * @param {String} text Text to encode.
28212      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28213      * @return {String} Entity encoded text.
28214      */
28215     encodeNumeric: function(text, attr) {
28216         var t = this;
28217         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28218             // Multi byte sequence convert it to a single entity
28219             if (chr.length > 1) {
28220                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28221             }
28222             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28223         });
28224     },
28225     /**
28226      * Encodes the specified string using named entities. The core entities will be encoded
28227      * as named ones but all non lower ascii characters will be encoded into named entities.
28228      *
28229      * @method encodeNamed
28230      * @param {String} text Text to encode.
28231      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28232      * @param {Object} entities Optional parameter with entities to use.
28233      * @return {String} Entity encoded text.
28234      */
28235     encodeNamed: function(text, attr, entities) {
28236         var t = this;
28237         entities = entities || this.namedEntities;
28238         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28239             return t.baseEntities[chr] || entities[chr] || chr;
28240         });
28241     },
28242     /**
28243      * Returns an encode function based on the name(s) and it's optional entities.
28244      *
28245      * @method getEncodeFunc
28246      * @param {String} name Comma separated list of encoders for example named,numeric.
28247      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28248      * @return {function} Encode function to be used.
28249      */
28250     getEncodeFunc: function(name, entities) {
28251         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28252         var t = this;
28253         function encodeNamedAndNumeric(text, attr) {
28254             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28255                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28256             });
28257         }
28258
28259         function encodeCustomNamed(text, attr) {
28260             return t.encodeNamed(text, attr, entities);
28261         }
28262         // Replace + with , to be compatible with previous TinyMCE versions
28263         name = this.makeMap(name.replace(/\+/g, ','));
28264         // Named and numeric encoder
28265         if (name.named && name.numeric) {
28266             return this.encodeNamedAndNumeric;
28267         }
28268         // Named encoder
28269         if (name.named) {
28270             // Custom names
28271             if (entities) {
28272                 return encodeCustomNamed;
28273             }
28274             return this.encodeNamed;
28275         }
28276         // Numeric
28277         if (name.numeric) {
28278             return this.encodeNumeric;
28279         }
28280         // Raw encoder
28281         return this.encodeRaw;
28282     },
28283     /**
28284      * Decodes the specified string, this will replace entities with raw UTF characters.
28285      *
28286      * @method decode
28287      * @param {String} text Text to entity decode.
28288      * @return {String} Entity decoded string.
28289      */
28290     decode: function(text)
28291     {
28292         var  t = this;
28293         return text.replace(this.entityRegExp, function(all, numeric) {
28294             if (numeric) {
28295                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28296                 // Support upper UTF
28297                 if (numeric > 65535) {
28298                     numeric -= 65536;
28299                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28300                 }
28301                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28302             }
28303             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28304         });
28305     },
28306     nativeDecode : function (text) {
28307         return text;
28308     },
28309     makeMap : function (items, delim, map) {
28310                 var i;
28311                 items = items || [];
28312                 delim = delim || ',';
28313                 if (typeof items == "string") {
28314                         items = items.split(delim);
28315                 }
28316                 map = map || {};
28317                 i = items.length;
28318                 while (i--) {
28319                         map[items[i]] = {};
28320                 }
28321                 return map;
28322         }
28323 };
28324     
28325     
28326     
28327 Roo.htmleditor.TidyEntities.init();
28328 /**
28329  * @class Roo.htmleditor.KeyEnter
28330  * Handle Enter press..
28331  * @cfg {Roo.HtmlEditorCore} core the editor.
28332  * @constructor
28333  * Create a new Filter.
28334  * @param {Object} config Configuration options
28335  */
28336
28337
28338
28339
28340
28341 Roo.htmleditor.KeyEnter = function(cfg) {
28342     Roo.apply(this, cfg);
28343     // this does not actually call walk as it's really just a abstract class
28344  
28345     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28346 }
28347
28348 //Roo.htmleditor.KeyEnter.i = 0;
28349
28350
28351 Roo.htmleditor.KeyEnter.prototype = {
28352     
28353     core : false,
28354     
28355     keypress : function(e)
28356     {
28357         if (e.charCode != 13 && e.charCode != 10) {
28358             Roo.log([e.charCode,e]);
28359             return true;
28360         }
28361         e.preventDefault();
28362         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28363         var doc = this.core.doc;
28364           //add a new line
28365        
28366     
28367         var sel = this.core.getSelection();
28368         var range = sel.getRangeAt(0);
28369         var n = range.commonAncestorContainer;
28370         var pc = range.closest([ 'ol', 'ul']);
28371         var pli = range.closest('li');
28372         if (!pc || e.ctrlKey) {
28373             // on it list, or ctrl pressed.
28374             if (!e.ctrlKey) {
28375                 sel.insertNode('br', 'after'); 
28376             } else {
28377                 // only do this if we have ctrl key..
28378                 var br = doc.createElement('br');
28379                 br.className = 'clear';
28380                 br.setAttribute('style', 'clear: both');
28381                 sel.insertNode(br, 'after'); 
28382             }
28383             
28384          
28385             this.core.undoManager.addEvent();
28386             this.core.fireEditorEvent(e);
28387             return false;
28388         }
28389         
28390         // deal with <li> insetion
28391         if (pli.innerText.trim() == '' &&
28392             pli.previousSibling &&
28393             pli.previousSibling.nodeName == 'LI' &&
28394             pli.previousSibling.innerText.trim() ==  '') {
28395             pli.parentNode.removeChild(pli.previousSibling);
28396             sel.cursorAfter(pc);
28397             this.core.undoManager.addEvent();
28398             this.core.fireEditorEvent(e);
28399             return false;
28400         }
28401     
28402         var li = doc.createElement('LI');
28403         li.innerHTML = '&nbsp;';
28404         if (!pli || !pli.firstSibling) {
28405             pc.appendChild(li);
28406         } else {
28407             pli.parentNode.insertBefore(li, pli.firstSibling);
28408         }
28409         sel.cursorText (li.firstChild);
28410       
28411         this.core.undoManager.addEvent();
28412         this.core.fireEditorEvent(e);
28413
28414         return false;
28415         
28416     
28417         
28418         
28419          
28420     }
28421 };
28422      
28423 /**
28424  * @class Roo.htmleditor.Block
28425  * Base class for html editor blocks - do not use it directly .. extend it..
28426  * @cfg {DomElement} node The node to apply stuff to.
28427  * @cfg {String} friendly_name the name that appears in the context bar about this block
28428  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28429  
28430  * @constructor
28431  * Create a new Filter.
28432  * @param {Object} config Configuration options
28433  */
28434
28435 Roo.htmleditor.Block  = function(cfg)
28436 {
28437     // do nothing .. should not be called really.
28438 }
28439 /**
28440  * factory method to get the block from an element (using cache if necessary)
28441  * @static
28442  * @param {HtmlElement} the dom element
28443  */
28444 Roo.htmleditor.Block.factory = function(node)
28445 {
28446     var cc = Roo.htmleditor.Block.cache;
28447     var id = Roo.get(node).id;
28448     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28449         Roo.htmleditor.Block.cache[id].readElement(node);
28450         return Roo.htmleditor.Block.cache[id];
28451     }
28452     var db  = node.getAttribute('data-block');
28453     if (!db) {
28454         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28455     }
28456     var cls = Roo.htmleditor['Block' + db];
28457     if (typeof(cls) == 'undefined') {
28458         //Roo.log(node.getAttribute('data-block'));
28459         Roo.log("OOps missing block : " + 'Block' + db);
28460         return false;
28461     }
28462     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28463     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28464 };
28465
28466 /**
28467  * initalize all Elements from content that are 'blockable'
28468  * @static
28469  * @param the body element
28470  */
28471 Roo.htmleditor.Block.initAll = function(body, type)
28472 {
28473     if (typeof(type) == 'undefined') {
28474         var ia = Roo.htmleditor.Block.initAll;
28475         ia(body,'table');
28476         ia(body,'td');
28477         ia(body,'figure');
28478         return;
28479     }
28480     Roo.each(Roo.get(body).query(type), function(e) {
28481         Roo.htmleditor.Block.factory(e);    
28482     },this);
28483 };
28484 // question goes here... do we need to clear out this cache sometimes?
28485 // or show we make it relivant to the htmleditor.
28486 Roo.htmleditor.Block.cache = {};
28487
28488 Roo.htmleditor.Block.prototype = {
28489     
28490     node : false,
28491     
28492      // used by context menu
28493     friendly_name : 'Based Block',
28494     
28495     // text for button to delete this element
28496     deleteTitle : false,
28497     
28498     context : false,
28499     /**
28500      * Update a node with values from this object
28501      * @param {DomElement} node
28502      */
28503     updateElement : function(node)
28504     {
28505         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28506     },
28507      /**
28508      * convert to plain HTML for calling insertAtCursor..
28509      */
28510     toHTML : function()
28511     {
28512         return Roo.DomHelper.markup(this.toObject());
28513     },
28514     /**
28515      * used by readEleemnt to extract data from a node
28516      * may need improving as it's pretty basic
28517      
28518      * @param {DomElement} node
28519      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28520      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28521      * @param {String} style the style property - eg. text-align
28522      */
28523     getVal : function(node, tag, attr, style)
28524     {
28525         var n = node;
28526         if (tag !== true && n.tagName != tag.toUpperCase()) {
28527             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28528             // but kiss for now.
28529             n = node.getElementsByTagName(tag).item(0);
28530         }
28531         if (!n) {
28532             return '';
28533         }
28534         if (attr === false) {
28535             return n;
28536         }
28537         if (attr == 'html') {
28538             return n.innerHTML;
28539         }
28540         if (attr == 'style') {
28541             return n.style[style]; 
28542         }
28543         
28544         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28545             
28546     },
28547     /**
28548      * create a DomHelper friendly object - for use with 
28549      * Roo.DomHelper.markup / overwrite / etc..
28550      * (override this)
28551      */
28552     toObject : function()
28553     {
28554         return {};
28555     },
28556       /**
28557      * Read a node that has a 'data-block' property - and extract the values from it.
28558      * @param {DomElement} node - the node
28559      */
28560     readElement : function(node)
28561     {
28562         
28563     } 
28564     
28565     
28566 };
28567
28568  
28569
28570 /**
28571  * @class Roo.htmleditor.BlockFigure
28572  * Block that has an image and a figcaption
28573  * @cfg {String} image_src the url for the image
28574  * @cfg {String} align (left|right) alignment for the block default left
28575  * @cfg {String} caption the text to appear below  (and in the alt tag)
28576  * @cfg {String} caption_display (block|none) display or not the caption
28577  * @cfg {String|number} image_width the width of the image number or %?
28578  * @cfg {String|number} image_height the height of the image number or %?
28579  * 
28580  * @constructor
28581  * Create a new Filter.
28582  * @param {Object} config Configuration options
28583  */
28584
28585 Roo.htmleditor.BlockFigure = function(cfg)
28586 {
28587     if (cfg.node) {
28588         this.readElement(cfg.node);
28589         this.updateElement(cfg.node);
28590     }
28591     Roo.apply(this, cfg);
28592 }
28593 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28594  
28595     
28596     // setable values.
28597     image_src: '',
28598     align: 'center',
28599     caption : '',
28600     caption_display : 'block',
28601     width : '100%',
28602     cls : '',
28603     href: '',
28604     video_url : '',
28605     
28606     // margin: '2%', not used
28607     
28608     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28609
28610     
28611     // used by context menu
28612     friendly_name : 'Image with caption',
28613     deleteTitle : "Delete Image and Caption",
28614     
28615     contextMenu : function(toolbar)
28616     {
28617         
28618         var block = function() {
28619             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28620         };
28621         
28622         
28623         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28624         
28625         var syncValue = toolbar.editorcore.syncValue;
28626         
28627         var fields = {};
28628         
28629         return [
28630              {
28631                 xtype : 'TextItem',
28632                 text : "Source: ",
28633                 xns : rooui.Toolbar  //Boostrap?
28634             },
28635             {
28636                 xtype : 'Button',
28637                 text: 'Change Image URL',
28638                  
28639                 listeners : {
28640                     click: function (btn, state)
28641                     {
28642                         var b = block();
28643                         
28644                         Roo.MessageBox.show({
28645                             title : "Image Source URL",
28646                             msg : "Enter the url for the image",
28647                             buttons: Roo.MessageBox.OKCANCEL,
28648                             fn: function(btn, val){
28649                                 if (btn != 'ok') {
28650                                     return;
28651                                 }
28652                                 b.image_src = val;
28653                                 b.updateElement();
28654                                 syncValue();
28655                                 toolbar.editorcore.onEditorEvent();
28656                             },
28657                             minWidth:250,
28658                             prompt:true,
28659                             //multiline: multiline,
28660                             modal : true,
28661                             value : b.image_src
28662                         });
28663                     }
28664                 },
28665                 xns : rooui.Toolbar
28666             },
28667          
28668             {
28669                 xtype : 'Button',
28670                 text: 'Change Link URL',
28671                  
28672                 listeners : {
28673                     click: function (btn, state)
28674                     {
28675                         var b = block();
28676                         
28677                         Roo.MessageBox.show({
28678                             title : "Link URL",
28679                             msg : "Enter the url for the link - leave blank to have no link",
28680                             buttons: Roo.MessageBox.OKCANCEL,
28681                             fn: function(btn, val){
28682                                 if (btn != 'ok') {
28683                                     return;
28684                                 }
28685                                 b.href = val;
28686                                 b.updateElement();
28687                                 syncValue();
28688                                 toolbar.editorcore.onEditorEvent();
28689                             },
28690                             minWidth:250,
28691                             prompt:true,
28692                             //multiline: multiline,
28693                             modal : true,
28694                             value : b.href
28695                         });
28696                     }
28697                 },
28698                 xns : rooui.Toolbar
28699             },
28700             {
28701                 xtype : 'Button',
28702                 text: 'Show Video URL',
28703                  
28704                 listeners : {
28705                     click: function (btn, state)
28706                     {
28707                         Roo.MessageBox.alert("Video URL",
28708                             block().video_url == '' ? 'This image is not linked ot a video' :
28709                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28710                     }
28711                 },
28712                 xns : rooui.Toolbar
28713             },
28714             
28715             
28716             {
28717                 xtype : 'TextItem',
28718                 text : "Width: ",
28719                 xns : rooui.Toolbar  //Boostrap?
28720             },
28721             {
28722                 xtype : 'ComboBox',
28723                 allowBlank : false,
28724                 displayField : 'val',
28725                 editable : true,
28726                 listWidth : 100,
28727                 triggerAction : 'all',
28728                 typeAhead : true,
28729                 valueField : 'val',
28730                 width : 70,
28731                 name : 'width',
28732                 listeners : {
28733                     select : function (combo, r, index)
28734                     {
28735                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28736                         var b = block();
28737                         b.width = r.get('val');
28738                         b.updateElement();
28739                         syncValue();
28740                         toolbar.editorcore.onEditorEvent();
28741                     }
28742                 },
28743                 xns : rooui.form,
28744                 store : {
28745                     xtype : 'SimpleStore',
28746                     data : [
28747                         ['100%'],
28748                         ['80%'],
28749                         ['50%'],
28750                         ['20%'],
28751                         ['10%']
28752                     ],
28753                     fields : [ 'val'],
28754                     xns : Roo.data
28755                 }
28756             },
28757             {
28758                 xtype : 'TextItem',
28759                 text : "Align: ",
28760                 xns : rooui.Toolbar  //Boostrap?
28761             },
28762             {
28763                 xtype : 'ComboBox',
28764                 allowBlank : false,
28765                 displayField : 'val',
28766                 editable : true,
28767                 listWidth : 100,
28768                 triggerAction : 'all',
28769                 typeAhead : true,
28770                 valueField : 'val',
28771                 width : 70,
28772                 name : 'align',
28773                 listeners : {
28774                     select : function (combo, r, index)
28775                     {
28776                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28777                         var b = block();
28778                         b.align = r.get('val');
28779                         b.updateElement();
28780                         syncValue();
28781                         toolbar.editorcore.onEditorEvent();
28782                     }
28783                 },
28784                 xns : rooui.form,
28785                 store : {
28786                     xtype : 'SimpleStore',
28787                     data : [
28788                         ['left'],
28789                         ['right'],
28790                         ['center']
28791                     ],
28792                     fields : [ 'val'],
28793                     xns : Roo.data
28794                 }
28795             },
28796             
28797             
28798             {
28799                 xtype : 'Button',
28800                 text: 'Hide Caption',
28801                 name : 'caption_display',
28802                 pressed : false,
28803                 enableToggle : true,
28804                 setValue : function(v) {
28805                     // this trigger toggle.
28806                      
28807                     this.setText(v ? "Hide Caption" : "Show Caption");
28808                     this.setPressed(v != 'block');
28809                 },
28810                 listeners : {
28811                     toggle: function (btn, state)
28812                     {
28813                         var b  = block();
28814                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28815                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28816                         b.updateElement();
28817                         syncValue();
28818                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28819                         toolbar.editorcore.onEditorEvent();
28820                     }
28821                 },
28822                 xns : rooui.Toolbar
28823             }
28824         ];
28825         
28826     },
28827     /**
28828      * create a DomHelper friendly object - for use with
28829      * Roo.DomHelper.markup / overwrite / etc..
28830      */
28831     toObject : function()
28832     {
28833         var d = document.createElement('div');
28834         d.innerHTML = this.caption;
28835         
28836         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28837         
28838         var iw = this.align == 'center' ? this.width : '100%';
28839         var img =   {
28840             tag : 'img',
28841             contenteditable : 'false',
28842             src : this.image_src,
28843             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28844             style: {
28845                 width : iw,
28846                 maxWidth : iw + ' !important', // this is not getting rendered?
28847                 margin : m  
28848                 
28849             }
28850         };
28851         /*
28852         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28853                     '<a href="{2}">' + 
28854                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28855                     '</a>' + 
28856                 '</div>',
28857         */
28858                 
28859         if (this.href.length > 0) {
28860             img = {
28861                 tag : 'a',
28862                 href: this.href,
28863                 contenteditable : 'true',
28864                 cn : [
28865                     img
28866                 ]
28867             };
28868         }
28869         
28870         
28871         if (this.video_url.length > 0) {
28872             img = {
28873                 tag : 'div',
28874                 cls : this.cls,
28875                 frameborder : 0,
28876                 allowfullscreen : true,
28877                 width : 420,  // these are for video tricks - that we replace the outer
28878                 height : 315,
28879                 src : this.video_url,
28880                 cn : [
28881                     img
28882                 ]
28883             };
28884         }
28885         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28886         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28887         
28888   
28889         var ret =   {
28890             tag: 'figure',
28891             'data-block' : 'Figure',
28892             'data-width' : this.width, 
28893             contenteditable : 'false',
28894             
28895             style : {
28896                 display: 'block',
28897                 float :  this.align ,
28898                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28899                 width : this.align == 'center' ? '100%' : this.width,
28900                 margin:  '0px',
28901                 padding: this.align == 'center' ? '0' : '0 10px' ,
28902                 textAlign : this.align   // seems to work for email..
28903                 
28904             },
28905            
28906             
28907             align : this.align,
28908             cn : [
28909                 img,
28910               
28911                 {
28912                     tag: 'figcaption',
28913                     'data-display' : this.caption_display,
28914                     style : {
28915                         textAlign : 'left',
28916                         fontSize : '16px',
28917                         lineHeight : '24px',
28918                         display : this.caption_display,
28919                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28920                         margin: m,
28921                         width: this.align == 'center' ?  this.width : '100%' 
28922                     
28923                          
28924                     },
28925                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28926                     cn : [
28927                         {
28928                             tag: 'div',
28929                             style  : {
28930                                 marginTop : '16px',
28931                                 textAlign : 'left'
28932                             },
28933                             align: 'left',
28934                             cn : [
28935                                 {
28936                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28937                                     tag : 'i',
28938                                     contenteditable : true,
28939                                     html : captionhtml
28940                                 }
28941                                 
28942                             ]
28943                         }
28944                         
28945                     ]
28946                     
28947                 }
28948             ]
28949         };
28950         return ret;
28951          
28952     },
28953     
28954     readElement : function(node)
28955     {
28956         // this should not really come from the link...
28957         this.video_url = this.getVal(node, 'div', 'src');
28958         this.cls = this.getVal(node, 'div', 'class');
28959         this.href = this.getVal(node, 'a', 'href');
28960         
28961         
28962         this.image_src = this.getVal(node, 'img', 'src');
28963          
28964         this.align = this.getVal(node, 'figure', 'align');
28965         var figcaption = this.getVal(node, 'figcaption', false);
28966         if (figcaption !== '') {
28967             this.caption = this.getVal(figcaption, 'i', 'html');
28968         }
28969         
28970
28971         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28972         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28973         this.width = this.getVal(node, true, 'data-width');
28974         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28975         
28976     },
28977     removeNode : function()
28978     {
28979         return this.node;
28980     }
28981     
28982   
28983    
28984      
28985     
28986     
28987     
28988     
28989 })
28990
28991  
28992
28993 /**
28994  * @class Roo.htmleditor.BlockTable
28995  * Block that manages a table
28996  * 
28997  * @constructor
28998  * Create a new Filter.
28999  * @param {Object} config Configuration options
29000  */
29001
29002 Roo.htmleditor.BlockTable = function(cfg)
29003 {
29004     if (cfg.node) {
29005         this.readElement(cfg.node);
29006         this.updateElement(cfg.node);
29007     }
29008     Roo.apply(this, cfg);
29009     if (!cfg.node) {
29010         this.rows = [];
29011         for(var r = 0; r < this.no_row; r++) {
29012             this.rows[r] = [];
29013             for(var c = 0; c < this.no_col; c++) {
29014                 this.rows[r][c] = this.emptyCell();
29015             }
29016         }
29017     }
29018     
29019     
29020 }
29021 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29022  
29023     rows : false,
29024     no_col : 1,
29025     no_row : 1,
29026     
29027     
29028     width: '100%',
29029     
29030     // used by context menu
29031     friendly_name : 'Table',
29032     deleteTitle : 'Delete Table',
29033     // context menu is drawn once..
29034     
29035     contextMenu : function(toolbar)
29036     {
29037         
29038         var block = function() {
29039             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29040         };
29041         
29042         
29043         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29044         
29045         var syncValue = toolbar.editorcore.syncValue;
29046         
29047         var fields = {};
29048         
29049         return [
29050             {
29051                 xtype : 'TextItem',
29052                 text : "Width: ",
29053                 xns : rooui.Toolbar  //Boostrap?
29054             },
29055             {
29056                 xtype : 'ComboBox',
29057                 allowBlank : false,
29058                 displayField : 'val',
29059                 editable : true,
29060                 listWidth : 100,
29061                 triggerAction : 'all',
29062                 typeAhead : true,
29063                 valueField : 'val',
29064                 width : 100,
29065                 name : 'width',
29066                 listeners : {
29067                     select : function (combo, r, index)
29068                     {
29069                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29070                         var b = block();
29071                         b.width = r.get('val');
29072                         b.updateElement();
29073                         syncValue();
29074                         toolbar.editorcore.onEditorEvent();
29075                     }
29076                 },
29077                 xns : rooui.form,
29078                 store : {
29079                     xtype : 'SimpleStore',
29080                     data : [
29081                         ['100%'],
29082                         ['auto']
29083                     ],
29084                     fields : [ 'val'],
29085                     xns : Roo.data
29086                 }
29087             },
29088             // -------- Cols
29089             
29090             {
29091                 xtype : 'TextItem',
29092                 text : "Columns: ",
29093                 xns : rooui.Toolbar  //Boostrap?
29094             },
29095          
29096             {
29097                 xtype : 'Button',
29098                 text: '-',
29099                 listeners : {
29100                     click : function (_self, e)
29101                     {
29102                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29103                         block().removeColumn();
29104                         syncValue();
29105                         toolbar.editorcore.onEditorEvent();
29106                     }
29107                 },
29108                 xns : rooui.Toolbar
29109             },
29110             {
29111                 xtype : 'Button',
29112                 text: '+',
29113                 listeners : {
29114                     click : function (_self, e)
29115                     {
29116                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29117                         block().addColumn();
29118                         syncValue();
29119                         toolbar.editorcore.onEditorEvent();
29120                     }
29121                 },
29122                 xns : rooui.Toolbar
29123             },
29124             // -------- ROWS
29125             {
29126                 xtype : 'TextItem',
29127                 text : "Rows: ",
29128                 xns : rooui.Toolbar  //Boostrap?
29129             },
29130          
29131             {
29132                 xtype : 'Button',
29133                 text: '-',
29134                 listeners : {
29135                     click : function (_self, e)
29136                     {
29137                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29138                         block().removeRow();
29139                         syncValue();
29140                         toolbar.editorcore.onEditorEvent();
29141                     }
29142                 },
29143                 xns : rooui.Toolbar
29144             },
29145             {
29146                 xtype : 'Button',
29147                 text: '+',
29148                 listeners : {
29149                     click : function (_self, e)
29150                     {
29151                         block().addRow();
29152                         syncValue();
29153                         toolbar.editorcore.onEditorEvent();
29154                     }
29155                 },
29156                 xns : rooui.Toolbar
29157             },
29158             // -------- ROWS
29159             {
29160                 xtype : 'Button',
29161                 text: 'Reset Column Widths',
29162                 listeners : {
29163                     
29164                     click : function (_self, e)
29165                     {
29166                         block().resetWidths();
29167                         syncValue();
29168                         toolbar.editorcore.onEditorEvent();
29169                     }
29170                 },
29171                 xns : rooui.Toolbar
29172             } 
29173             
29174             
29175             
29176         ];
29177         
29178     },
29179     
29180     
29181   /**
29182      * create a DomHelper friendly object - for use with
29183      * Roo.DomHelper.markup / overwrite / etc..
29184      * ?? should it be called with option to hide all editing features?
29185      */
29186     toObject : function()
29187     {
29188         
29189         var ret = {
29190             tag : 'table',
29191             contenteditable : 'false', // this stops cell selection from picking the table.
29192             'data-block' : 'Table',
29193             style : {
29194                 width:  this.width,
29195                 border : 'solid 1px #000', // ??? hard coded?
29196                 'border-collapse' : 'collapse' 
29197             },
29198             cn : [
29199                 { tag : 'tbody' , cn : [] }
29200             ]
29201         };
29202         
29203         // do we have a head = not really 
29204         var ncols = 0;
29205         Roo.each(this.rows, function( row ) {
29206             var tr = {
29207                 tag: 'tr',
29208                 style : {
29209                     margin: '6px',
29210                     border : 'solid 1px #000',
29211                     textAlign : 'left' 
29212                 },
29213                 cn : [ ]
29214             };
29215             
29216             ret.cn[0].cn.push(tr);
29217             // does the row have any properties? ?? height?
29218             var nc = 0;
29219             Roo.each(row, function( cell ) {
29220                 
29221                 var td = {
29222                     tag : 'td',
29223                     contenteditable :  'true',
29224                     'data-block' : 'Td',
29225                     html : cell.html,
29226                     style : cell.style
29227                 };
29228                 if (cell.colspan > 1) {
29229                     td.colspan = cell.colspan ;
29230                     nc += cell.colspan;
29231                 } else {
29232                     nc++;
29233                 }
29234                 if (cell.rowspan > 1) {
29235                     td.rowspan = cell.rowspan ;
29236                 }
29237                 
29238                 
29239                 // widths ?
29240                 tr.cn.push(td);
29241                     
29242                 
29243             }, this);
29244             ncols = Math.max(nc, ncols);
29245             
29246             
29247         }, this);
29248         // add the header row..
29249         
29250         ncols++;
29251          
29252         
29253         return ret;
29254          
29255     },
29256     
29257     readElement : function(node)
29258     {
29259         node  = node ? node : this.node ;
29260         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29261         
29262         this.rows = [];
29263         this.no_row = 0;
29264         var trs = Array.from(node.rows);
29265         trs.forEach(function(tr) {
29266             var row =  [];
29267             this.rows.push(row);
29268             
29269             this.no_row++;
29270             var no_column = 0;
29271             Array.from(tr.cells).forEach(function(td) {
29272                 
29273                 var add = {
29274                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29275                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29276                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29277                     html : td.innerHTML
29278                 };
29279                 no_column += add.colspan;
29280                      
29281                 
29282                 row.push(add);
29283                 
29284                 
29285             },this);
29286             this.no_col = Math.max(this.no_col, no_column);
29287             
29288             
29289         },this);
29290         
29291         
29292     },
29293     normalizeRows: function()
29294     {
29295         var ret= [];
29296         var rid = -1;
29297         this.rows.forEach(function(row) {
29298             rid++;
29299             ret[rid] = [];
29300             row = this.normalizeRow(row);
29301             var cid = 0;
29302             row.forEach(function(c) {
29303                 while (typeof(ret[rid][cid]) != 'undefined') {
29304                     cid++;
29305                 }
29306                 if (typeof(ret[rid]) == 'undefined') {
29307                     ret[rid] = [];
29308                 }
29309                 ret[rid][cid] = c;
29310                 c.row = rid;
29311                 c.col = cid;
29312                 if (c.rowspan < 2) {
29313                     return;
29314                 }
29315                 
29316                 for(var i = 1 ;i < c.rowspan; i++) {
29317                     if (typeof(ret[rid+i]) == 'undefined') {
29318                         ret[rid+i] = [];
29319                     }
29320                     ret[rid+i][cid] = c;
29321                 }
29322             });
29323         }, this);
29324         return ret;
29325     
29326     },
29327     
29328     normalizeRow: function(row)
29329     {
29330         var ret= [];
29331         row.forEach(function(c) {
29332             if (c.colspan < 2) {
29333                 ret.push(c);
29334                 return;
29335             }
29336             for(var i =0 ;i < c.colspan; i++) {
29337                 ret.push(c);
29338             }
29339         });
29340         return ret;
29341     
29342     },
29343     
29344     deleteColumn : function(sel)
29345     {
29346         if (!sel || sel.type != 'col') {
29347             return;
29348         }
29349         if (this.no_col < 2) {
29350             return;
29351         }
29352         
29353         this.rows.forEach(function(row) {
29354             var cols = this.normalizeRow(row);
29355             var col = cols[sel.col];
29356             if (col.colspan > 1) {
29357                 col.colspan --;
29358             } else {
29359                 row.remove(col);
29360             }
29361             
29362         }, this);
29363         this.no_col--;
29364         
29365     },
29366     removeColumn : function()
29367     {
29368         this.deleteColumn({
29369             type: 'col',
29370             col : this.no_col-1
29371         });
29372         this.updateElement();
29373     },
29374     
29375      
29376     addColumn : function()
29377     {
29378         
29379         this.rows.forEach(function(row) {
29380             row.push(this.emptyCell());
29381            
29382         }, this);
29383         this.updateElement();
29384     },
29385     
29386     deleteRow : function(sel)
29387     {
29388         if (!sel || sel.type != 'row') {
29389             return;
29390         }
29391         
29392         if (this.no_row < 2) {
29393             return;
29394         }
29395         
29396         var rows = this.normalizeRows();
29397         
29398         
29399         rows[sel.row].forEach(function(col) {
29400             if (col.rowspan > 1) {
29401                 col.rowspan--;
29402             } else {
29403                 col.remove = 1; // flage it as removed.
29404             }
29405             
29406         }, this);
29407         var newrows = [];
29408         this.rows.forEach(function(row) {
29409             newrow = [];
29410             row.forEach(function(c) {
29411                 if (typeof(c.remove) == 'undefined') {
29412                     newrow.push(c);
29413                 }
29414                 
29415             });
29416             if (newrow.length > 0) {
29417                 newrows.push(row);
29418             }
29419         });
29420         this.rows =  newrows;
29421         
29422         
29423         
29424         this.no_row--;
29425         this.updateElement();
29426         
29427     },
29428     removeRow : function()
29429     {
29430         this.deleteRow({
29431             type: 'row',
29432             row : this.no_row-1
29433         });
29434         
29435     },
29436     
29437      
29438     addRow : function()
29439     {
29440         
29441         var row = [];
29442         for (var i = 0; i < this.no_col; i++ ) {
29443             
29444             row.push(this.emptyCell());
29445            
29446         }
29447         this.rows.push(row);
29448         this.updateElement();
29449         
29450     },
29451      
29452     // the default cell object... at present...
29453     emptyCell : function() {
29454         return (new Roo.htmleditor.BlockTd({})).toObject();
29455         
29456      
29457     },
29458     
29459     removeNode : function()
29460     {
29461         return this.node;
29462     },
29463     
29464     
29465     
29466     resetWidths : function()
29467     {
29468         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29469             var nn = Roo.htmleditor.Block.factory(n);
29470             nn.width = '';
29471             nn.updateElement(n);
29472         });
29473     }
29474     
29475     
29476     
29477     
29478 })
29479
29480 /**
29481  *
29482  * editing a TD?
29483  *
29484  * since selections really work on the table cell, then editing really should work from there
29485  *
29486  * The original plan was to support merging etc... - but that may not be needed yet..
29487  *
29488  * So this simple version will support:
29489  *   add/remove cols
29490  *   adjust the width +/-
29491  *   reset the width...
29492  *   
29493  *
29494  */
29495
29496
29497  
29498
29499 /**
29500  * @class Roo.htmleditor.BlockTable
29501  * Block that manages a table
29502  * 
29503  * @constructor
29504  * Create a new Filter.
29505  * @param {Object} config Configuration options
29506  */
29507
29508 Roo.htmleditor.BlockTd = function(cfg)
29509 {
29510     if (cfg.node) {
29511         this.readElement(cfg.node);
29512         this.updateElement(cfg.node);
29513     }
29514     Roo.apply(this, cfg);
29515      
29516     
29517     
29518 }
29519 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29520  
29521     node : false,
29522     
29523     width: '',
29524     textAlign : 'left',
29525     valign : 'top',
29526     
29527     colspan : 1,
29528     rowspan : 1,
29529     
29530     
29531     // used by context menu
29532     friendly_name : 'Table Cell',
29533     deleteTitle : false, // use our customer delete
29534     
29535     // context menu is drawn once..
29536     
29537     contextMenu : function(toolbar)
29538     {
29539         
29540         var cell = function() {
29541             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29542         };
29543         
29544         var table = function() {
29545             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29546         };
29547         
29548         var lr = false;
29549         var saveSel = function()
29550         {
29551             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29552         }
29553         var restoreSel = function()
29554         {
29555             if (lr) {
29556                 (function() {
29557                     toolbar.editorcore.focus();
29558                     var cr = toolbar.editorcore.getSelection();
29559                     cr.removeAllRanges();
29560                     cr.addRange(lr);
29561                     toolbar.editorcore.onEditorEvent();
29562                 }).defer(10, this);
29563                 
29564                 
29565             }
29566         }
29567         
29568         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29569         
29570         var syncValue = toolbar.editorcore.syncValue;
29571         
29572         var fields = {};
29573         
29574         return [
29575             {
29576                 xtype : 'Button',
29577                 text : 'Edit Table',
29578                 listeners : {
29579                     click : function() {
29580                         var t = toolbar.tb.selectedNode.closest('table');
29581                         toolbar.editorcore.selectNode(t);
29582                         toolbar.editorcore.onEditorEvent();                        
29583                     }
29584                 }
29585                 
29586             },
29587               
29588            
29589              
29590             {
29591                 xtype : 'TextItem',
29592                 text : "Column Width: ",
29593                  xns : rooui.Toolbar 
29594                
29595             },
29596             {
29597                 xtype : 'Button',
29598                 text: '-',
29599                 listeners : {
29600                     click : function (_self, e)
29601                     {
29602                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29603                         cell().shrinkColumn();
29604                         syncValue();
29605                          toolbar.editorcore.onEditorEvent();
29606                     }
29607                 },
29608                 xns : rooui.Toolbar
29609             },
29610             {
29611                 xtype : 'Button',
29612                 text: '+',
29613                 listeners : {
29614                     click : function (_self, e)
29615                     {
29616                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29617                         cell().growColumn();
29618                         syncValue();
29619                         toolbar.editorcore.onEditorEvent();
29620                     }
29621                 },
29622                 xns : rooui.Toolbar
29623             },
29624             
29625             {
29626                 xtype : 'TextItem',
29627                 text : "Vertical Align: ",
29628                 xns : rooui.Toolbar  //Boostrap?
29629             },
29630             {
29631                 xtype : 'ComboBox',
29632                 allowBlank : false,
29633                 displayField : 'val',
29634                 editable : true,
29635                 listWidth : 100,
29636                 triggerAction : 'all',
29637                 typeAhead : true,
29638                 valueField : 'val',
29639                 width : 100,
29640                 name : 'valign',
29641                 listeners : {
29642                     select : function (combo, r, index)
29643                     {
29644                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29645                         var b = cell();
29646                         b.valign = r.get('val');
29647                         b.updateElement();
29648                         syncValue();
29649                         toolbar.editorcore.onEditorEvent();
29650                     }
29651                 },
29652                 xns : rooui.form,
29653                 store : {
29654                     xtype : 'SimpleStore',
29655                     data : [
29656                         ['top'],
29657                         ['middle'],
29658                         ['bottom'] // there are afew more... 
29659                     ],
29660                     fields : [ 'val'],
29661                     xns : Roo.data
29662                 }
29663             },
29664             
29665             {
29666                 xtype : 'TextItem',
29667                 text : "Merge Cells: ",
29668                  xns : rooui.Toolbar 
29669                
29670             },
29671             
29672             
29673             {
29674                 xtype : 'Button',
29675                 text: 'Right',
29676                 listeners : {
29677                     click : function (_self, e)
29678                     {
29679                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29680                         cell().mergeRight();
29681                         //block().growColumn();
29682                         syncValue();
29683                         toolbar.editorcore.onEditorEvent();
29684                     }
29685                 },
29686                 xns : rooui.Toolbar
29687             },
29688              
29689             {
29690                 xtype : 'Button',
29691                 text: 'Below',
29692                 listeners : {
29693                     click : function (_self, e)
29694                     {
29695                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29696                         cell().mergeBelow();
29697                         //block().growColumn();
29698                         syncValue();
29699                         toolbar.editorcore.onEditorEvent();
29700                     }
29701                 },
29702                 xns : rooui.Toolbar
29703             },
29704             {
29705                 xtype : 'TextItem',
29706                 text : "| ",
29707                  xns : rooui.Toolbar 
29708                
29709             },
29710             
29711             {
29712                 xtype : 'Button',
29713                 text: 'Split',
29714                 listeners : {
29715                     click : function (_self, e)
29716                     {
29717                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29718                         cell().split();
29719                         syncValue();
29720                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29721                         toolbar.editorcore.onEditorEvent();
29722                                              
29723                     }
29724                 },
29725                 xns : rooui.Toolbar
29726             },
29727             {
29728                 xtype : 'Fill',
29729                 xns : rooui.Toolbar 
29730                
29731             },
29732         
29733           
29734             {
29735                 xtype : 'Button',
29736                 text: 'Delete',
29737                  
29738                 xns : rooui.Toolbar,
29739                 menu : {
29740                     xtype : 'Menu',
29741                     xns : rooui.menu,
29742                     items : [
29743                         {
29744                             xtype : 'Item',
29745                             html: 'Column',
29746                             listeners : {
29747                                 click : function (_self, e)
29748                                 {
29749                                     var t = table();
29750                                     
29751                                     cell().deleteColumn();
29752                                     syncValue();
29753                                     toolbar.editorcore.selectNode(t.node);
29754                                     toolbar.editorcore.onEditorEvent();   
29755                                 }
29756                             },
29757                             xns : rooui.menu
29758                         },
29759                         {
29760                             xtype : 'Item',
29761                             html: 'Row',
29762                             listeners : {
29763                                 click : function (_self, e)
29764                                 {
29765                                     var t = table();
29766                                     cell().deleteRow();
29767                                     syncValue();
29768                                     
29769                                     toolbar.editorcore.selectNode(t.node);
29770                                     toolbar.editorcore.onEditorEvent();   
29771                                                          
29772                                 }
29773                             },
29774                             xns : rooui.menu
29775                         },
29776                        {
29777                             xtype : 'Separator',
29778                             xns : rooui.menu
29779                         },
29780                         {
29781                             xtype : 'Item',
29782                             html: 'Table',
29783                             listeners : {
29784                                 click : function (_self, e)
29785                                 {
29786                                     var t = table();
29787                                     var nn = t.node.nextSibling || t.node.previousSibling;
29788                                     t.node.parentNode.removeChild(t.node);
29789                                     if (nn) { 
29790                                         toolbar.editorcore.selectNode(nn, true);
29791                                     }
29792                                     toolbar.editorcore.onEditorEvent();   
29793                                                          
29794                                 }
29795                             },
29796                             xns : rooui.menu
29797                         }
29798                     ]
29799                 }
29800             }
29801             
29802             // align... << fixme
29803             
29804         ];
29805         
29806     },
29807     
29808     
29809   /**
29810      * create a DomHelper friendly object - for use with
29811      * Roo.DomHelper.markup / overwrite / etc..
29812      * ?? should it be called with option to hide all editing features?
29813      */
29814  /**
29815      * create a DomHelper friendly object - for use with
29816      * Roo.DomHelper.markup / overwrite / etc..
29817      * ?? should it be called with option to hide all editing features?
29818      */
29819     toObject : function()
29820     {
29821         var ret = {
29822             tag : 'td',
29823             contenteditable : 'true', // this stops cell selection from picking the table.
29824             'data-block' : 'Td',
29825             valign : this.valign,
29826             style : {  
29827                 'text-align' :  this.textAlign,
29828                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29829                 'border-collapse' : 'collapse',
29830                 padding : '6px', // 8 for desktop / 4 for mobile
29831                 'vertical-align': this.valign
29832             },
29833             html : this.html
29834         };
29835         if (this.width != '') {
29836             ret.width = this.width;
29837             ret.style.width = this.width;
29838         }
29839         
29840         
29841         if (this.colspan > 1) {
29842             ret.colspan = this.colspan ;
29843         } 
29844         if (this.rowspan > 1) {
29845             ret.rowspan = this.rowspan ;
29846         }
29847         
29848            
29849         
29850         return ret;
29851          
29852     },
29853     
29854     readElement : function(node)
29855     {
29856         node  = node ? node : this.node ;
29857         this.width = node.style.width;
29858         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29859         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29860         this.html = node.innerHTML;
29861         if (node.style.textAlign != '') {
29862             this.textAlign = node.style.textAlign;
29863         }
29864         
29865         
29866     },
29867      
29868     // the default cell object... at present...
29869     emptyCell : function() {
29870         return {
29871             colspan :  1,
29872             rowspan :  1,
29873             textAlign : 'left',
29874             html : "&nbsp;" // is this going to be editable now?
29875         };
29876      
29877     },
29878     
29879     removeNode : function()
29880     {
29881         return this.node.closest('table');
29882          
29883     },
29884     
29885     cellData : false,
29886     
29887     colWidths : false,
29888     
29889     toTableArray  : function()
29890     {
29891         var ret = [];
29892         var tab = this.node.closest('tr').closest('table');
29893         Array.from(tab.rows).forEach(function(r, ri){
29894             ret[ri] = [];
29895         });
29896         var rn = 0;
29897         this.colWidths = [];
29898         var all_auto = true;
29899         Array.from(tab.rows).forEach(function(r, ri){
29900             
29901             var cn = 0;
29902             Array.from(r.cells).forEach(function(ce, ci){
29903                 var c =  {
29904                     cell : ce,
29905                     row : rn,
29906                     col: cn,
29907                     colspan : ce.colSpan,
29908                     rowspan : ce.rowSpan
29909                 };
29910                 if (ce.isEqualNode(this.node)) {
29911                     this.cellData = c;
29912                 }
29913                 // if we have been filled up by a row?
29914                 if (typeof(ret[rn][cn]) != 'undefined') {
29915                     while(typeof(ret[rn][cn]) != 'undefined') {
29916                         cn++;
29917                     }
29918                     c.col = cn;
29919                 }
29920                 
29921                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29922                     this.colWidths[cn] =   ce.style.width;
29923                     if (this.colWidths[cn] != '') {
29924                         all_auto = false;
29925                     }
29926                 }
29927                 
29928                 
29929                 if (c.colspan < 2 && c.rowspan < 2 ) {
29930                     ret[rn][cn] = c;
29931                     cn++;
29932                     return;
29933                 }
29934                 for(var j = 0; j < c.rowspan; j++) {
29935                     if (typeof(ret[rn+j]) == 'undefined') {
29936                         continue; // we have a problem..
29937                     }
29938                     ret[rn+j][cn] = c;
29939                     for(var i = 0; i < c.colspan; i++) {
29940                         ret[rn+j][cn+i] = c;
29941                     }
29942                 }
29943                 
29944                 cn += c.colspan;
29945             }, this);
29946             rn++;
29947         }, this);
29948         
29949         // initalize widths.?
29950         // either all widths or no widths..
29951         if (all_auto) {
29952             this.colWidths[0] = false; // no widths flag.
29953         }
29954         
29955         
29956         return ret;
29957         
29958     },
29959     
29960     
29961     
29962     
29963     mergeRight: function()
29964     {
29965          
29966         // get the contents of the next cell along..
29967         var tr = this.node.closest('tr');
29968         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29969         if (i >= tr.childNodes.length - 1) {
29970             return; // no cells on right to merge with.
29971         }
29972         var table = this.toTableArray();
29973         
29974         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29975             return; // nothing right?
29976         }
29977         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29978         // right cell - must be same rowspan and on the same row.
29979         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29980             return; // right hand side is not same rowspan.
29981         }
29982         
29983         
29984         
29985         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29986         tr.removeChild(rc.cell);
29987         this.colspan += rc.colspan;
29988         this.node.setAttribute('colspan', this.colspan);
29989
29990         var table = this.toTableArray();
29991         this.normalizeWidths(table);
29992         this.updateWidths(table);
29993     },
29994     
29995     
29996     mergeBelow : function()
29997     {
29998         var table = this.toTableArray();
29999         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30000             return; // no row below
30001         }
30002         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30003             return; // nothing right?
30004         }
30005         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30006         
30007         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30008             return; // right hand side is not same rowspan.
30009         }
30010         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30011         rc.cell.parentNode.removeChild(rc.cell);
30012         this.rowspan += rc.rowspan;
30013         this.node.setAttribute('rowspan', this.rowspan);
30014     },
30015     
30016     split: function()
30017     {
30018         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30019             return;
30020         }
30021         var table = this.toTableArray();
30022         var cd = this.cellData;
30023         this.rowspan = 1;
30024         this.colspan = 1;
30025         
30026         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30027              
30028             
30029             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30030                 if (r == cd.row && c == cd.col) {
30031                     this.node.removeAttribute('rowspan');
30032                     this.node.removeAttribute('colspan');
30033                 }
30034                  
30035                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30036                 ntd.removeAttribute('id'); 
30037                 ntd.style.width  = this.colWidths[c];
30038                 ntd.innerHTML = '';
30039                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30040             }
30041             
30042         }
30043         this.redrawAllCells(table);
30044         
30045     },
30046     
30047     
30048     
30049     redrawAllCells: function(table)
30050     {
30051         
30052          
30053         var tab = this.node.closest('tr').closest('table');
30054         var ctr = tab.rows[0].parentNode;
30055         Array.from(tab.rows).forEach(function(r, ri){
30056             
30057             Array.from(r.cells).forEach(function(ce, ci){
30058                 ce.parentNode.removeChild(ce);
30059             });
30060             r.parentNode.removeChild(r);
30061         });
30062         for(var r = 0 ; r < table.length; r++) {
30063             var re = tab.rows[r];
30064             
30065             var re = tab.ownerDocument.createElement('tr');
30066             ctr.appendChild(re);
30067             for(var c = 0 ; c < table[r].length; c++) {
30068                 if (table[r][c].cell === false) {
30069                     continue;
30070                 }
30071                 
30072                 re.appendChild(table[r][c].cell);
30073                  
30074                 table[r][c].cell = false;
30075             }
30076         }
30077         
30078     },
30079     updateWidths : function(table)
30080     {
30081         for(var r = 0 ; r < table.length; r++) {
30082            
30083             for(var c = 0 ; c < table[r].length; c++) {
30084                 if (table[r][c].cell === false) {
30085                     continue;
30086                 }
30087                 
30088                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30089                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30090                     el.width = Math.floor(this.colWidths[c])  +'%';
30091                     el.updateElement(el.node);
30092                 }
30093                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30094                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30095                     var width = 0;
30096                     for(var i = 0; i < table[r][c].colspan; i ++) {
30097                         width += Math.floor(this.colWidths[c + i]);
30098                     }
30099                     el.width = width  +'%';
30100                     el.updateElement(el.node);
30101                 }
30102                 table[r][c].cell = false; // done
30103             }
30104         }
30105     },
30106     normalizeWidths : function(table)
30107     {
30108         if (this.colWidths[0] === false) {
30109             var nw = 100.0 / this.colWidths.length;
30110             this.colWidths.forEach(function(w,i) {
30111                 this.colWidths[i] = nw;
30112             },this);
30113             return;
30114         }
30115     
30116         var t = 0, missing = [];
30117         
30118         this.colWidths.forEach(function(w,i) {
30119             //if you mix % and
30120             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30121             var add =  this.colWidths[i];
30122             if (add > 0) {
30123                 t+=add;
30124                 return;
30125             }
30126             missing.push(i);
30127             
30128             
30129         },this);
30130         var nc = this.colWidths.length;
30131         if (missing.length) {
30132             var mult = (nc - missing.length) / (1.0 * nc);
30133             var t = mult * t;
30134             var ew = (100 -t) / (1.0 * missing.length);
30135             this.colWidths.forEach(function(w,i) {
30136                 if (w > 0) {
30137                     this.colWidths[i] = w * mult;
30138                     return;
30139                 }
30140                 
30141                 this.colWidths[i] = ew;
30142             }, this);
30143             // have to make up numbers..
30144              
30145         }
30146         // now we should have all the widths..
30147         
30148     
30149     },
30150     
30151     shrinkColumn : function()
30152     {
30153         var table = this.toTableArray();
30154         this.normalizeWidths(table);
30155         var col = this.cellData.col;
30156         var nw = this.colWidths[col] * 0.8;
30157         if (nw < 5) {
30158             return;
30159         }
30160         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30161         this.colWidths.forEach(function(w,i) {
30162             if (i == col) {
30163                  this.colWidths[i] = nw;
30164                 return;
30165             }
30166             this.colWidths[i] += otherAdd
30167         }, this);
30168         this.updateWidths(table);
30169          
30170     },
30171     growColumn : function()
30172     {
30173         var table = this.toTableArray();
30174         this.normalizeWidths(table);
30175         var col = this.cellData.col;
30176         var nw = this.colWidths[col] * 1.2;
30177         if (nw > 90) {
30178             return;
30179         }
30180         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30181         this.colWidths.forEach(function(w,i) {
30182             if (i == col) {
30183                 this.colWidths[i] = nw;
30184                 return;
30185             }
30186             this.colWidths[i] -= otherSub
30187         }, this);
30188         this.updateWidths(table);
30189          
30190     },
30191     deleteRow : function()
30192     {
30193         // delete this rows 'tr'
30194         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30195         // then reduce the rowspan.
30196         var table = this.toTableArray();
30197         // this.cellData.row;
30198         for (var i =0;i< table[this.cellData.row].length ; i++) {
30199             var c = table[this.cellData.row][i];
30200             if (c.row != this.cellData.row) {
30201                 
30202                 c.rowspan--;
30203                 c.cell.setAttribute('rowspan', c.rowspan);
30204                 continue;
30205             }
30206             if (c.rowspan > 1) {
30207                 c.rowspan--;
30208                 c.cell.setAttribute('rowspan', c.rowspan);
30209             }
30210         }
30211         table.splice(this.cellData.row,1);
30212         this.redrawAllCells(table);
30213         
30214     },
30215     deleteColumn : function()
30216     {
30217         var table = this.toTableArray();
30218         
30219         for (var i =0;i< table.length ; i++) {
30220             var c = table[i][this.cellData.col];
30221             if (c.col != this.cellData.col) {
30222                 table[i][this.cellData.col].colspan--;
30223             } else if (c.colspan > 1) {
30224                 c.colspan--;
30225                 c.cell.setAttribute('colspan', c.colspan);
30226             }
30227             table[i].splice(this.cellData.col,1);
30228         }
30229         
30230         this.redrawAllCells(table);
30231     }
30232     
30233     
30234     
30235     
30236 })
30237
30238 //<script type="text/javascript">
30239
30240 /*
30241  * Based  Ext JS Library 1.1.1
30242  * Copyright(c) 2006-2007, Ext JS, LLC.
30243  * LGPL
30244  *
30245  */
30246  
30247 /**
30248  * @class Roo.HtmlEditorCore
30249  * @extends Roo.Component
30250  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30251  *
30252  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30253  */
30254
30255 Roo.HtmlEditorCore = function(config){
30256     
30257     
30258     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30259     
30260     
30261     this.addEvents({
30262         /**
30263          * @event initialize
30264          * Fires when the editor is fully initialized (including the iframe)
30265          * @param {Roo.HtmlEditorCore} this
30266          */
30267         initialize: true,
30268         /**
30269          * @event activate
30270          * Fires when the editor is first receives the focus. Any insertion must wait
30271          * until after this event.
30272          * @param {Roo.HtmlEditorCore} this
30273          */
30274         activate: true,
30275          /**
30276          * @event beforesync
30277          * Fires before the textarea is updated with content from the editor iframe. Return false
30278          * to cancel the sync.
30279          * @param {Roo.HtmlEditorCore} this
30280          * @param {String} html
30281          */
30282         beforesync: true,
30283          /**
30284          * @event beforepush
30285          * Fires before the iframe editor is updated with content from the textarea. Return false
30286          * to cancel the push.
30287          * @param {Roo.HtmlEditorCore} this
30288          * @param {String} html
30289          */
30290         beforepush: true,
30291          /**
30292          * @event sync
30293          * Fires when the textarea is updated with content from the editor iframe.
30294          * @param {Roo.HtmlEditorCore} this
30295          * @param {String} html
30296          */
30297         sync: true,
30298          /**
30299          * @event push
30300          * Fires when the iframe editor is updated with content from the textarea.
30301          * @param {Roo.HtmlEditorCore} this
30302          * @param {String} html
30303          */
30304         push: true,
30305         
30306         /**
30307          * @event editorevent
30308          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30309          * @param {Roo.HtmlEditorCore} this
30310          */
30311         editorevent: true 
30312          
30313         
30314     });
30315     
30316     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30317     
30318     // defaults : white / black...
30319     this.applyBlacklists();
30320     
30321     
30322     
30323 };
30324
30325
30326 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30327
30328
30329      /**
30330      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30331      */
30332     
30333     owner : false,
30334     
30335      /**
30336      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30337      *                        Roo.resizable.
30338      */
30339     resizable : false,
30340      /**
30341      * @cfg {Number} height (in pixels)
30342      */   
30343     height: 300,
30344    /**
30345      * @cfg {Number} width (in pixels)
30346      */   
30347     width: 500,
30348      /**
30349      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30350      *         if you are doing an email editor, this probably needs disabling, it's designed
30351      */
30352     autoClean: true,
30353     
30354     /**
30355      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30356      */
30357     enableBlocks : true,
30358     /**
30359      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30360      * 
30361      */
30362     stylesheets: false,
30363      /**
30364      * @cfg {String} language default en - language of text (usefull for rtl languages)
30365      * 
30366      */
30367     language: 'en',
30368     
30369     /**
30370      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30371      *          - by default they are stripped - if you are editing email you may need this.
30372      */
30373     allowComments: false,
30374     // id of frame..
30375     frameId: false,
30376     
30377     // private properties
30378     validationEvent : false,
30379     deferHeight: true,
30380     initialized : false,
30381     activated : false,
30382     sourceEditMode : false,
30383     onFocus : Roo.emptyFn,
30384     iframePad:3,
30385     hideMode:'offsets',
30386     
30387     clearUp: true,
30388     
30389     // blacklist + whitelisted elements..
30390     black: false,
30391     white: false,
30392      
30393     bodyCls : '',
30394
30395     
30396     undoManager : false,
30397     /**
30398      * Protected method that will not generally be called directly. It
30399      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30400      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30401      */
30402     getDocMarkup : function(){
30403         // body styles..
30404         var st = '';
30405         
30406         // inherit styels from page...?? 
30407         if (this.stylesheets === false) {
30408             
30409             Roo.get(document.head).select('style').each(function(node) {
30410                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30411             });
30412             
30413             Roo.get(document.head).select('link').each(function(node) { 
30414                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30415             });
30416             
30417         } else if (!this.stylesheets.length) {
30418                 // simple..
30419                 st = '<style type="text/css">' +
30420                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30421                    '</style>';
30422         } else {
30423             for (var i in this.stylesheets) {
30424                 if (typeof(this.stylesheets[i]) != 'string') {
30425                     continue;
30426                 }
30427                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30428             }
30429             
30430         }
30431         
30432         st +=  '<style type="text/css">' +
30433             'IMG { cursor: pointer } ' +
30434         '</style>';
30435         
30436         st += '<meta name="google" content="notranslate">';
30437         
30438         var cls = 'notranslate roo-htmleditor-body';
30439         
30440         if(this.bodyCls.length){
30441             cls += ' ' + this.bodyCls;
30442         }
30443         
30444         return '<html  class="notranslate" translate="no"><head>' + st  +
30445             //<style type="text/css">' +
30446             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30447             //'</style>' +
30448             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30449     },
30450
30451     // private
30452     onRender : function(ct, position)
30453     {
30454         var _t = this;
30455         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30456         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30457         
30458         
30459         this.el.dom.style.border = '0 none';
30460         this.el.dom.setAttribute('tabIndex', -1);
30461         this.el.addClass('x-hidden hide');
30462         
30463         
30464         
30465         if(Roo.isIE){ // fix IE 1px bogus margin
30466             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30467         }
30468        
30469         
30470         this.frameId = Roo.id();
30471         
30472          
30473         
30474         var iframe = this.owner.wrap.createChild({
30475             tag: 'iframe',
30476             cls: 'form-control', // bootstrap..
30477             id: this.frameId,
30478             name: this.frameId,
30479             frameBorder : 'no',
30480             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30481         }, this.el
30482         );
30483         
30484         
30485         this.iframe = iframe.dom;
30486
30487         this.assignDocWin();
30488         
30489         this.doc.designMode = 'on';
30490        
30491         this.doc.open();
30492         this.doc.write(this.getDocMarkup());
30493         this.doc.close();
30494
30495         
30496         var task = { // must defer to wait for browser to be ready
30497             run : function(){
30498                 //console.log("run task?" + this.doc.readyState);
30499                 this.assignDocWin();
30500                 if(this.doc.body || this.doc.readyState == 'complete'){
30501                     try {
30502                         this.doc.designMode="on";
30503                         
30504                     } catch (e) {
30505                         return;
30506                     }
30507                     Roo.TaskMgr.stop(task);
30508                     this.initEditor.defer(10, this);
30509                 }
30510             },
30511             interval : 10,
30512             duration: 10000,
30513             scope: this
30514         };
30515         Roo.TaskMgr.start(task);
30516
30517     },
30518
30519     // private
30520     onResize : function(w, h)
30521     {
30522          Roo.log('resize: ' +w + ',' + h );
30523         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30524         if(!this.iframe){
30525             return;
30526         }
30527         if(typeof w == 'number'){
30528             
30529             this.iframe.style.width = w + 'px';
30530         }
30531         if(typeof h == 'number'){
30532             
30533             this.iframe.style.height = h + 'px';
30534             if(this.doc){
30535                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30536             }
30537         }
30538         
30539     },
30540
30541     /**
30542      * Toggles the editor between standard and source edit mode.
30543      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30544      */
30545     toggleSourceEdit : function(sourceEditMode){
30546         
30547         this.sourceEditMode = sourceEditMode === true;
30548         
30549         if(this.sourceEditMode){
30550  
30551             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30552             
30553         }else{
30554             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30555             //this.iframe.className = '';
30556             this.deferFocus();
30557         }
30558         //this.setSize(this.owner.wrap.getSize());
30559         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30560     },
30561
30562     
30563   
30564
30565     /**
30566      * Protected method that will not generally be called directly. If you need/want
30567      * custom HTML cleanup, this is the method you should override.
30568      * @param {String} html The HTML to be cleaned
30569      * return {String} The cleaned HTML
30570      */
30571     cleanHtml : function(html)
30572     {
30573         html = String(html);
30574         if(html.length > 5){
30575             if(Roo.isSafari){ // strip safari nonsense
30576                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30577             }
30578         }
30579         if(html == '&nbsp;'){
30580             html = '';
30581         }
30582         return html;
30583     },
30584
30585     /**
30586      * HTML Editor -> Textarea
30587      * Protected method that will not generally be called directly. Syncs the contents
30588      * of the editor iframe with the textarea.
30589      */
30590     syncValue : function()
30591     {
30592         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30593         if(this.initialized){
30594             
30595             if (this.undoManager) {
30596                 this.undoManager.addEvent();
30597             }
30598
30599             
30600             var bd = (this.doc.body || this.doc.documentElement);
30601            
30602             
30603             var sel = this.win.getSelection();
30604             
30605             var div = document.createElement('div');
30606             div.innerHTML = bd.innerHTML;
30607             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30608             if (gtx.length > 0) {
30609                 var rm = gtx.item(0).parentNode;
30610                 rm.parentNode.removeChild(rm);
30611             }
30612             
30613            
30614             if (this.enableBlocks) {
30615                 new Roo.htmleditor.FilterBlock({ node : div });
30616             }
30617             
30618             var html = div.innerHTML;
30619             
30620             //?? tidy?
30621             if (this.autoClean) {
30622                 
30623                 new Roo.htmleditor.FilterAttributes({
30624                     node : div,
30625                     attrib_white : [
30626                             'href',
30627                             'src',
30628                             'name',
30629                             'align',
30630                             'colspan',
30631                             'rowspan',
30632                             'data-display',
30633                             'data-width',
30634                             'start' ,
30635                             'style',
30636                             // youtube embed.
30637                             'class',
30638                             'allowfullscreen',
30639                             'frameborder',
30640                             'width',
30641                             'height',
30642                             'alt'
30643                             ],
30644                     attrib_clean : ['href', 'src' ] 
30645                 });
30646                 
30647                 var tidy = new Roo.htmleditor.TidySerializer({
30648                     inner:  true
30649                 });
30650                 html  = tidy.serialize(div);
30651                 
30652             }
30653             
30654             
30655             if(Roo.isSafari){
30656                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30657                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30658                 if(m && m[1]){
30659                     html = '<div style="'+m[0]+'">' + html + '</div>';
30660                 }
30661             }
30662             html = this.cleanHtml(html);
30663             // fix up the special chars.. normaly like back quotes in word...
30664             // however we do not want to do this with chinese..
30665             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30666                 
30667                 var cc = match.charCodeAt();
30668
30669                 // Get the character value, handling surrogate pairs
30670                 if (match.length == 2) {
30671                     // It's a surrogate pair, calculate the Unicode code point
30672                     var high = match.charCodeAt(0) - 0xD800;
30673                     var low  = match.charCodeAt(1) - 0xDC00;
30674                     cc = (high * 0x400) + low + 0x10000;
30675                 }  else if (
30676                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30677                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30678                     (cc >= 0xf900 && cc < 0xfb00 )
30679                 ) {
30680                         return match;
30681                 }  
30682          
30683                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30684                 return "&#" + cc + ";";
30685                 
30686                 
30687             });
30688             
30689             
30690              
30691             if(this.owner.fireEvent('beforesync', this, html) !== false){
30692                 this.el.dom.value = html;
30693                 this.owner.fireEvent('sync', this, html);
30694             }
30695         }
30696     },
30697
30698     /**
30699      * TEXTAREA -> EDITABLE
30700      * Protected method that will not generally be called directly. Pushes the value of the textarea
30701      * into the iframe editor.
30702      */
30703     pushValue : function()
30704     {
30705         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30706         if(this.initialized){
30707             var v = this.el.dom.value.trim();
30708             
30709             
30710             if(this.owner.fireEvent('beforepush', this, v) !== false){
30711                 var d = (this.doc.body || this.doc.documentElement);
30712                 d.innerHTML = v;
30713                  
30714                 this.el.dom.value = d.innerHTML;
30715                 this.owner.fireEvent('push', this, v);
30716             }
30717             if (this.autoClean) {
30718                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30719                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30720             }
30721             if (this.enableBlocks) {
30722                 Roo.htmleditor.Block.initAll(this.doc.body);
30723             }
30724             
30725             this.updateLanguage();
30726             
30727             var lc = this.doc.body.lastChild;
30728             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30729                 // add an extra line at the end.
30730                 this.doc.body.appendChild(this.doc.createElement('br'));
30731             }
30732             
30733             
30734         }
30735     },
30736
30737     // private
30738     deferFocus : function(){
30739         this.focus.defer(10, this);
30740     },
30741
30742     // doc'ed in Field
30743     focus : function(){
30744         if(this.win && !this.sourceEditMode){
30745             this.win.focus();
30746         }else{
30747             this.el.focus();
30748         }
30749     },
30750     
30751     assignDocWin: function()
30752     {
30753         var iframe = this.iframe;
30754         
30755          if(Roo.isIE){
30756             this.doc = iframe.contentWindow.document;
30757             this.win = iframe.contentWindow;
30758         } else {
30759 //            if (!Roo.get(this.frameId)) {
30760 //                return;
30761 //            }
30762 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30763 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30764             
30765             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30766                 return;
30767             }
30768             
30769             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30770             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30771         }
30772     },
30773     
30774     // private
30775     initEditor : function(){
30776         //console.log("INIT EDITOR");
30777         this.assignDocWin();
30778         
30779         
30780         
30781         this.doc.designMode="on";
30782         this.doc.open();
30783         this.doc.write(this.getDocMarkup());
30784         this.doc.close();
30785         
30786         var dbody = (this.doc.body || this.doc.documentElement);
30787         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30788         // this copies styles from the containing element into thsi one..
30789         // not sure why we need all of this..
30790         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30791         
30792         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30793         //ss['background-attachment'] = 'fixed'; // w3c
30794         dbody.bgProperties = 'fixed'; // ie
30795         dbody.setAttribute("translate", "no");
30796         
30797         //Roo.DomHelper.applyStyles(dbody, ss);
30798         Roo.EventManager.on(this.doc, {
30799              
30800             'mouseup': this.onEditorEvent,
30801             'dblclick': this.onEditorEvent,
30802             'click': this.onEditorEvent,
30803             'keyup': this.onEditorEvent,
30804             
30805             buffer:100,
30806             scope: this
30807         });
30808         Roo.EventManager.on(this.doc, {
30809             'paste': this.onPasteEvent,
30810             scope : this
30811         });
30812         if(Roo.isGecko){
30813             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30814         }
30815         //??? needed???
30816         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30817             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30818         }
30819         this.initialized = true;
30820
30821         
30822         // initialize special key events - enter
30823         new Roo.htmleditor.KeyEnter({core : this});
30824         
30825          
30826         
30827         this.owner.fireEvent('initialize', this);
30828         this.pushValue();
30829     },
30830     // this is to prevent a href clicks resulting in a redirect?
30831    
30832     onPasteEvent : function(e,v)
30833     {
30834         // I think we better assume paste is going to be a dirty load of rubish from word..
30835         
30836         // even pasting into a 'email version' of this widget will have to clean up that mess.
30837         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30838         
30839         // check what type of paste - if it's an image, then handle it differently.
30840         if (cd.files && cd.files.length > 0) {
30841             // pasting images?
30842             var urlAPI = (window.createObjectURL && window) || 
30843                 (window.URL && URL.revokeObjectURL && URL) || 
30844                 (window.webkitURL && webkitURL);
30845     
30846             var url = urlAPI.createObjectURL( cd.files[0]);
30847             this.insertAtCursor('<img src=" + url + ">');
30848             return false;
30849         }
30850         if (cd.types.indexOf('text/html') < 0 ) {
30851             return false;
30852         }
30853         var images = [];
30854         var html = cd.getData('text/html'); // clipboard event
30855         if (cd.types.indexOf('text/rtf') > -1) {
30856             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30857             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30858         }
30859         //Roo.log(images);
30860         //Roo.log(imgs);
30861         // fixme..
30862         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30863                        .map(function(g) { return g.toDataURL(); })
30864                        .filter(function(g) { return g != 'about:blank'; });
30865         
30866         //Roo.log(html);
30867         html = this.cleanWordChars(html);
30868         
30869         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30870         
30871         
30872         var sn = this.getParentElement();
30873         // check if d contains a table, and prevent nesting??
30874         //Roo.log(d.getElementsByTagName('table'));
30875         //Roo.log(sn);
30876         //Roo.log(sn.closest('table'));
30877         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30878             e.preventDefault();
30879             this.insertAtCursor("You can not nest tables");
30880             //Roo.log("prevent?"); // fixme - 
30881             return false;
30882         }
30883         
30884         
30885         
30886         if (images.length > 0) {
30887             // replace all v:imagedata - with img.
30888             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30889             Roo.each(ar, function(node) {
30890                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30891                 node.parentNode.removeChild(node);
30892             });
30893             
30894             
30895             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30896                 img.setAttribute('src', images[i]);
30897             });
30898         }
30899         if (this.autoClean) {
30900             new Roo.htmleditor.FilterWord({ node : d });
30901             
30902             new Roo.htmleditor.FilterStyleToTag({ node : d });
30903             new Roo.htmleditor.FilterAttributes({
30904                 node : d,
30905                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30906                 attrib_clean : ['href', 'src' ] 
30907             });
30908             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30909             // should be fonts..
30910             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30911             new Roo.htmleditor.FilterParagraph({ node : d });
30912             new Roo.htmleditor.FilterSpan({ node : d });
30913             new Roo.htmleditor.FilterLongBr({ node : d });
30914             new Roo.htmleditor.FilterComment({ node : d });
30915             
30916             
30917         }
30918         if (this.enableBlocks) {
30919                 
30920             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30921                 if (img.closest('figure')) { // assume!! that it's aready
30922                     return;
30923                 }
30924                 var fig  = new Roo.htmleditor.BlockFigure({
30925                     image_src  : img.src
30926                 });
30927                 fig.updateElement(img); // replace it..
30928                 
30929             });
30930         }
30931         
30932         
30933         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30934         if (this.enableBlocks) {
30935             Roo.htmleditor.Block.initAll(this.doc.body);
30936         }
30937          
30938         
30939         e.preventDefault();
30940         return false;
30941         // default behaveiour should be our local cleanup paste? (optional?)
30942         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30943         //this.owner.fireEvent('paste', e, v);
30944     },
30945     // private
30946     onDestroy : function(){
30947         
30948         
30949         
30950         if(this.rendered){
30951             
30952             //for (var i =0; i < this.toolbars.length;i++) {
30953             //    // fixme - ask toolbars for heights?
30954             //    this.toolbars[i].onDestroy();
30955            // }
30956             
30957             //this.wrap.dom.innerHTML = '';
30958             //this.wrap.remove();
30959         }
30960     },
30961
30962     // private
30963     onFirstFocus : function(){
30964         
30965         this.assignDocWin();
30966         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30967         
30968         this.activated = true;
30969          
30970     
30971         if(Roo.isGecko){ // prevent silly gecko errors
30972             this.win.focus();
30973             var s = this.win.getSelection();
30974             if(!s.focusNode || s.focusNode.nodeType != 3){
30975                 var r = s.getRangeAt(0);
30976                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30977                 r.collapse(true);
30978                 this.deferFocus();
30979             }
30980             try{
30981                 this.execCmd('useCSS', true);
30982                 this.execCmd('styleWithCSS', false);
30983             }catch(e){}
30984         }
30985         this.owner.fireEvent('activate', this);
30986     },
30987
30988     // private
30989     adjustFont: function(btn){
30990         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30991         //if(Roo.isSafari){ // safari
30992         //    adjust *= 2;
30993        // }
30994         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30995         if(Roo.isSafari){ // safari
30996             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30997             v =  (v < 10) ? 10 : v;
30998             v =  (v > 48) ? 48 : v;
30999             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31000             
31001         }
31002         
31003         
31004         v = Math.max(1, v+adjust);
31005         
31006         this.execCmd('FontSize', v  );
31007     },
31008
31009     onEditorEvent : function(e)
31010     {
31011          
31012         
31013         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31014             return; // we do not handle this.. (undo manager does..)
31015         }
31016         // in theory this detects if the last element is not a br, then we try and do that.
31017         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31018         if (e &&
31019             e.target.nodeName == 'BODY' &&
31020             e.type == "mouseup" &&
31021             this.doc.body.lastChild
31022            ) {
31023             var lc = this.doc.body.lastChild;
31024             // gtx-trans is google translate plugin adding crap.
31025             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31026                 lc = lc.previousSibling;
31027             }
31028             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31029             // if last element is <BR> - then dont do anything.
31030             
31031                 var ns = this.doc.createElement('br');
31032                 this.doc.body.appendChild(ns);
31033                 range = this.doc.createRange();
31034                 range.setStartAfter(ns);
31035                 range.collapse(true);
31036                 var sel = this.win.getSelection();
31037                 sel.removeAllRanges();
31038                 sel.addRange(range);
31039             }
31040         }
31041         
31042         
31043         
31044         this.fireEditorEvent(e);
31045       //  this.updateToolbar();
31046         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31047     },
31048     
31049     fireEditorEvent: function(e)
31050     {
31051         this.owner.fireEvent('editorevent', this, e);
31052     },
31053
31054     insertTag : function(tg)
31055     {
31056         // could be a bit smarter... -> wrap the current selected tRoo..
31057         if (tg.toLowerCase() == 'span' ||
31058             tg.toLowerCase() == 'code' ||
31059             tg.toLowerCase() == 'sup' ||
31060             tg.toLowerCase() == 'sub' 
31061             ) {
31062             
31063             range = this.createRange(this.getSelection());
31064             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31065             wrappingNode.appendChild(range.extractContents());
31066             range.insertNode(wrappingNode);
31067
31068             return;
31069             
31070             
31071             
31072         }
31073         this.execCmd("formatblock",   tg);
31074         this.undoManager.addEvent(); 
31075     },
31076     
31077     insertText : function(txt)
31078     {
31079         
31080         
31081         var range = this.createRange();
31082         range.deleteContents();
31083                //alert(Sender.getAttribute('label'));
31084                
31085         range.insertNode(this.doc.createTextNode(txt));
31086         this.undoManager.addEvent();
31087     } ,
31088     
31089      
31090
31091     /**
31092      * Executes a Midas editor command on the editor document and performs necessary focus and
31093      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31094      * @param {String} cmd The Midas command
31095      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31096      */
31097     relayCmd : function(cmd, value)
31098     {
31099         
31100         switch (cmd) {
31101             case 'justifyleft':
31102             case 'justifyright':
31103             case 'justifycenter':
31104                 // if we are in a cell, then we will adjust the
31105                 var n = this.getParentElement();
31106                 var td = n.closest('td');
31107                 if (td) {
31108                     var bl = Roo.htmleditor.Block.factory(td);
31109                     bl.textAlign = cmd.replace('justify','');
31110                     bl.updateElement();
31111                     this.owner.fireEvent('editorevent', this);
31112                     return;
31113                 }
31114                 this.execCmd('styleWithCSS', true); // 
31115                 break;
31116             case 'bold':
31117             case 'italic':
31118                 // if there is no selection, then we insert, and set the curson inside it..
31119                 this.execCmd('styleWithCSS', false); 
31120                 break;
31121                 
31122         
31123             default:
31124                 break;
31125         }
31126         
31127         
31128         this.win.focus();
31129         this.execCmd(cmd, value);
31130         this.owner.fireEvent('editorevent', this);
31131         //this.updateToolbar();
31132         this.owner.deferFocus();
31133     },
31134
31135     /**
31136      * Executes a Midas editor command directly on the editor document.
31137      * For visual commands, you should use {@link #relayCmd} instead.
31138      * <b>This should only be called after the editor is initialized.</b>
31139      * @param {String} cmd The Midas command
31140      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31141      */
31142     execCmd : function(cmd, value){
31143         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31144         this.syncValue();
31145     },
31146  
31147  
31148    
31149     /**
31150      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31151      * to insert tRoo.
31152      * @param {String} text | dom node.. 
31153      */
31154     insertAtCursor : function(text)
31155     {
31156         
31157         if(!this.activated){
31158             return;
31159         }
31160          
31161         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31162             this.win.focus();
31163             
31164             
31165             // from jquery ui (MIT licenced)
31166             var range, node;
31167             var win = this.win;
31168             
31169             if (win.getSelection && win.getSelection().getRangeAt) {
31170                 
31171                 // delete the existing?
31172                 
31173                 this.createRange(this.getSelection()).deleteContents();
31174                 range = win.getSelection().getRangeAt(0);
31175                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31176                 range.insertNode(node);
31177                 range = range.cloneRange();
31178                 range.collapse(false);
31179                  
31180                 win.getSelection().removeAllRanges();
31181                 win.getSelection().addRange(range);
31182                 
31183                 
31184                 
31185             } else if (win.document.selection && win.document.selection.createRange) {
31186                 // no firefox support
31187                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31188                 win.document.selection.createRange().pasteHTML(txt);
31189             
31190             } else {
31191                 // no firefox support
31192                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31193                 this.execCmd('InsertHTML', txt);
31194             } 
31195             this.syncValue();
31196             
31197             this.deferFocus();
31198         }
31199     },
31200  // private
31201     mozKeyPress : function(e){
31202         if(e.ctrlKey){
31203             var c = e.getCharCode(), cmd;
31204           
31205             if(c > 0){
31206                 c = String.fromCharCode(c).toLowerCase();
31207                 switch(c){
31208                     case 'b':
31209                         cmd = 'bold';
31210                         break;
31211                     case 'i':
31212                         cmd = 'italic';
31213                         break;
31214                     
31215                     case 'u':
31216                         cmd = 'underline';
31217                         break;
31218                     
31219                     //case 'v':
31220                       //  this.cleanUpPaste.defer(100, this);
31221                       //  return;
31222                         
31223                 }
31224                 if(cmd){
31225                     
31226                     this.relayCmd(cmd);
31227                     //this.win.focus();
31228                     //this.execCmd(cmd);
31229                     //this.deferFocus();
31230                     e.preventDefault();
31231                 }
31232                 
31233             }
31234         }
31235     },
31236
31237     // private
31238     fixKeys : function(){ // load time branching for fastest keydown performance
31239         
31240         
31241         if(Roo.isIE){
31242             return function(e){
31243                 var k = e.getKey(), r;
31244                 if(k == e.TAB){
31245                     e.stopEvent();
31246                     r = this.doc.selection.createRange();
31247                     if(r){
31248                         r.collapse(true);
31249                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31250                         this.deferFocus();
31251                     }
31252                     return;
31253                 }
31254                 /// this is handled by Roo.htmleditor.KeyEnter
31255                  /*
31256                 if(k == e.ENTER){
31257                     r = this.doc.selection.createRange();
31258                     if(r){
31259                         var target = r.parentElement();
31260                         if(!target || target.tagName.toLowerCase() != 'li'){
31261                             e.stopEvent();
31262                             r.pasteHTML('<br/>');
31263                             r.collapse(false);
31264                             r.select();
31265                         }
31266                     }
31267                 }
31268                 */
31269                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31270                 //    this.cleanUpPaste.defer(100, this);
31271                 //    return;
31272                 //}
31273                 
31274                 
31275             };
31276         }else if(Roo.isOpera){
31277             return function(e){
31278                 var k = e.getKey();
31279                 if(k == e.TAB){
31280                     e.stopEvent();
31281                     this.win.focus();
31282                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31283                     this.deferFocus();
31284                 }
31285                
31286                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31287                 //    this.cleanUpPaste.defer(100, this);
31288                  //   return;
31289                 //}
31290                 
31291             };
31292         }else if(Roo.isSafari){
31293             return function(e){
31294                 var k = e.getKey();
31295                 
31296                 if(k == e.TAB){
31297                     e.stopEvent();
31298                     this.execCmd('InsertText','\t');
31299                     this.deferFocus();
31300                     return;
31301                 }
31302                  this.mozKeyPress(e);
31303                 
31304                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31305                  //   this.cleanUpPaste.defer(100, this);
31306                  //   return;
31307                // }
31308                 
31309              };
31310         }
31311     }(),
31312     
31313     getAllAncestors: function()
31314     {
31315         var p = this.getSelectedNode();
31316         var a = [];
31317         if (!p) {
31318             a.push(p); // push blank onto stack..
31319             p = this.getParentElement();
31320         }
31321         
31322         
31323         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31324             a.push(p);
31325             p = p.parentNode;
31326         }
31327         a.push(this.doc.body);
31328         return a;
31329     },
31330     lastSel : false,
31331     lastSelNode : false,
31332     
31333     
31334     getSelection : function() 
31335     {
31336         this.assignDocWin();
31337         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31338     },
31339     /**
31340      * Select a dom node
31341      * @param {DomElement} node the node to select
31342      */
31343     selectNode : function(node, collapse)
31344     {
31345         var nodeRange = node.ownerDocument.createRange();
31346         try {
31347             nodeRange.selectNode(node);
31348         } catch (e) {
31349             nodeRange.selectNodeContents(node);
31350         }
31351         if (collapse === true) {
31352             nodeRange.collapse(true);
31353         }
31354         //
31355         var s = this.win.getSelection();
31356         s.removeAllRanges();
31357         s.addRange(nodeRange);
31358     },
31359     
31360     getSelectedNode: function() 
31361     {
31362         // this may only work on Gecko!!!
31363         
31364         // should we cache this!!!!
31365         
31366          
31367          
31368         var range = this.createRange(this.getSelection()).cloneRange();
31369         
31370         if (Roo.isIE) {
31371             var parent = range.parentElement();
31372             while (true) {
31373                 var testRange = range.duplicate();
31374                 testRange.moveToElementText(parent);
31375                 if (testRange.inRange(range)) {
31376                     break;
31377                 }
31378                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31379                     break;
31380                 }
31381                 parent = parent.parentElement;
31382             }
31383             return parent;
31384         }
31385         
31386         // is ancestor a text element.
31387         var ac =  range.commonAncestorContainer;
31388         if (ac.nodeType == 3) {
31389             ac = ac.parentNode;
31390         }
31391         
31392         var ar = ac.childNodes;
31393          
31394         var nodes = [];
31395         var other_nodes = [];
31396         var has_other_nodes = false;
31397         for (var i=0;i<ar.length;i++) {
31398             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31399                 continue;
31400             }
31401             // fullly contained node.
31402             
31403             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31404                 nodes.push(ar[i]);
31405                 continue;
31406             }
31407             
31408             // probably selected..
31409             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31410                 other_nodes.push(ar[i]);
31411                 continue;
31412             }
31413             // outer..
31414             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31415                 continue;
31416             }
31417             
31418             
31419             has_other_nodes = true;
31420         }
31421         if (!nodes.length && other_nodes.length) {
31422             nodes= other_nodes;
31423         }
31424         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31425             return false;
31426         }
31427         
31428         return nodes[0];
31429     },
31430     
31431     
31432     createRange: function(sel)
31433     {
31434         // this has strange effects when using with 
31435         // top toolbar - not sure if it's a great idea.
31436         //this.editor.contentWindow.focus();
31437         if (typeof sel != "undefined") {
31438             try {
31439                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31440             } catch(e) {
31441                 return this.doc.createRange();
31442             }
31443         } else {
31444             return this.doc.createRange();
31445         }
31446     },
31447     getParentElement: function()
31448     {
31449         
31450         this.assignDocWin();
31451         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31452         
31453         var range = this.createRange(sel);
31454          
31455         try {
31456             var p = range.commonAncestorContainer;
31457             while (p.nodeType == 3) { // text node
31458                 p = p.parentNode;
31459             }
31460             return p;
31461         } catch (e) {
31462             return null;
31463         }
31464     
31465     },
31466     /***
31467      *
31468      * Range intersection.. the hard stuff...
31469      *  '-1' = before
31470      *  '0' = hits..
31471      *  '1' = after.
31472      *         [ -- selected range --- ]
31473      *   [fail]                        [fail]
31474      *
31475      *    basically..
31476      *      if end is before start or  hits it. fail.
31477      *      if start is after end or hits it fail.
31478      *
31479      *   if either hits (but other is outside. - then it's not 
31480      *   
31481      *    
31482      **/
31483     
31484     
31485     // @see http://www.thismuchiknow.co.uk/?p=64.
31486     rangeIntersectsNode : function(range, node)
31487     {
31488         var nodeRange = node.ownerDocument.createRange();
31489         try {
31490             nodeRange.selectNode(node);
31491         } catch (e) {
31492             nodeRange.selectNodeContents(node);
31493         }
31494     
31495         var rangeStartRange = range.cloneRange();
31496         rangeStartRange.collapse(true);
31497     
31498         var rangeEndRange = range.cloneRange();
31499         rangeEndRange.collapse(false);
31500     
31501         var nodeStartRange = nodeRange.cloneRange();
31502         nodeStartRange.collapse(true);
31503     
31504         var nodeEndRange = nodeRange.cloneRange();
31505         nodeEndRange.collapse(false);
31506     
31507         return rangeStartRange.compareBoundaryPoints(
31508                  Range.START_TO_START, nodeEndRange) == -1 &&
31509                rangeEndRange.compareBoundaryPoints(
31510                  Range.START_TO_START, nodeStartRange) == 1;
31511         
31512          
31513     },
31514     rangeCompareNode : function(range, node)
31515     {
31516         var nodeRange = node.ownerDocument.createRange();
31517         try {
31518             nodeRange.selectNode(node);
31519         } catch (e) {
31520             nodeRange.selectNodeContents(node);
31521         }
31522         
31523         
31524         range.collapse(true);
31525     
31526         nodeRange.collapse(true);
31527      
31528         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31529         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31530          
31531         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31532         
31533         var nodeIsBefore   =  ss == 1;
31534         var nodeIsAfter    = ee == -1;
31535         
31536         if (nodeIsBefore && nodeIsAfter) {
31537             return 0; // outer
31538         }
31539         if (!nodeIsBefore && nodeIsAfter) {
31540             return 1; //right trailed.
31541         }
31542         
31543         if (nodeIsBefore && !nodeIsAfter) {
31544             return 2;  // left trailed.
31545         }
31546         // fully contined.
31547         return 3;
31548     },
31549  
31550     cleanWordChars : function(input) {// change the chars to hex code
31551         
31552        var swapCodes  = [ 
31553             [    8211, "&#8211;" ], 
31554             [    8212, "&#8212;" ], 
31555             [    8216,  "'" ],  
31556             [    8217, "'" ],  
31557             [    8220, '"' ],  
31558             [    8221, '"' ],  
31559             [    8226, "*" ],  
31560             [    8230, "..." ]
31561         ]; 
31562         var output = input;
31563         Roo.each(swapCodes, function(sw) { 
31564             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31565             
31566             output = output.replace(swapper, sw[1]);
31567         });
31568         
31569         return output;
31570     },
31571     
31572      
31573     
31574         
31575     
31576     cleanUpChild : function (node)
31577     {
31578         
31579         new Roo.htmleditor.FilterComment({node : node});
31580         new Roo.htmleditor.FilterAttributes({
31581                 node : node,
31582                 attrib_black : this.ablack,
31583                 attrib_clean : this.aclean,
31584                 style_white : this.cwhite,
31585                 style_black : this.cblack
31586         });
31587         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31588         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31589          
31590         
31591     },
31592     
31593     /**
31594      * Clean up MS wordisms...
31595      * @deprecated - use filter directly
31596      */
31597     cleanWord : function(node)
31598     {
31599         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31600         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31601         
31602     },
31603    
31604     
31605     /**
31606
31607      * @deprecated - use filters
31608      */
31609     cleanTableWidths : function(node)
31610     {
31611         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31612         
31613  
31614     },
31615     
31616      
31617         
31618     applyBlacklists : function()
31619     {
31620         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31621         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31622         
31623         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31624         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31625         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31626         
31627         this.white = [];
31628         this.black = [];
31629         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31630             if (b.indexOf(tag) > -1) {
31631                 return;
31632             }
31633             this.white.push(tag);
31634             
31635         }, this);
31636         
31637         Roo.each(w, function(tag) {
31638             if (b.indexOf(tag) > -1) {
31639                 return;
31640             }
31641             if (this.white.indexOf(tag) > -1) {
31642                 return;
31643             }
31644             this.white.push(tag);
31645             
31646         }, this);
31647         
31648         
31649         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31650             if (w.indexOf(tag) > -1) {
31651                 return;
31652             }
31653             this.black.push(tag);
31654             
31655         }, this);
31656         
31657         Roo.each(b, function(tag) {
31658             if (w.indexOf(tag) > -1) {
31659                 return;
31660             }
31661             if (this.black.indexOf(tag) > -1) {
31662                 return;
31663             }
31664             this.black.push(tag);
31665             
31666         }, this);
31667         
31668         
31669         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31670         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31671         
31672         this.cwhite = [];
31673         this.cblack = [];
31674         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31675             if (b.indexOf(tag) > -1) {
31676                 return;
31677             }
31678             this.cwhite.push(tag);
31679             
31680         }, this);
31681         
31682         Roo.each(w, function(tag) {
31683             if (b.indexOf(tag) > -1) {
31684                 return;
31685             }
31686             if (this.cwhite.indexOf(tag) > -1) {
31687                 return;
31688             }
31689             this.cwhite.push(tag);
31690             
31691         }, this);
31692         
31693         
31694         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31695             if (w.indexOf(tag) > -1) {
31696                 return;
31697             }
31698             this.cblack.push(tag);
31699             
31700         }, this);
31701         
31702         Roo.each(b, function(tag) {
31703             if (w.indexOf(tag) > -1) {
31704                 return;
31705             }
31706             if (this.cblack.indexOf(tag) > -1) {
31707                 return;
31708             }
31709             this.cblack.push(tag);
31710             
31711         }, this);
31712     },
31713     
31714     setStylesheets : function(stylesheets)
31715     {
31716         if(typeof(stylesheets) == 'string'){
31717             Roo.get(this.iframe.contentDocument.head).createChild({
31718                 tag : 'link',
31719                 rel : 'stylesheet',
31720                 type : 'text/css',
31721                 href : stylesheets
31722             });
31723             
31724             return;
31725         }
31726         var _this = this;
31727      
31728         Roo.each(stylesheets, function(s) {
31729             if(!s.length){
31730                 return;
31731             }
31732             
31733             Roo.get(_this.iframe.contentDocument.head).createChild({
31734                 tag : 'link',
31735                 rel : 'stylesheet',
31736                 type : 'text/css',
31737                 href : s
31738             });
31739         });
31740
31741         
31742     },
31743     
31744     
31745     updateLanguage : function()
31746     {
31747         if (!this.iframe || !this.iframe.contentDocument) {
31748             return;
31749         }
31750         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31751     },
31752     
31753     
31754     removeStylesheets : function()
31755     {
31756         var _this = this;
31757         
31758         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31759             s.remove();
31760         });
31761     },
31762     
31763     setStyle : function(style)
31764     {
31765         Roo.get(this.iframe.contentDocument.head).createChild({
31766             tag : 'style',
31767             type : 'text/css',
31768             html : style
31769         });
31770
31771         return;
31772     }
31773     
31774     // hide stuff that is not compatible
31775     /**
31776      * @event blur
31777      * @hide
31778      */
31779     /**
31780      * @event change
31781      * @hide
31782      */
31783     /**
31784      * @event focus
31785      * @hide
31786      */
31787     /**
31788      * @event specialkey
31789      * @hide
31790      */
31791     /**
31792      * @cfg {String} fieldClass @hide
31793      */
31794     /**
31795      * @cfg {String} focusClass @hide
31796      */
31797     /**
31798      * @cfg {String} autoCreate @hide
31799      */
31800     /**
31801      * @cfg {String} inputType @hide
31802      */
31803     /**
31804      * @cfg {String} invalidClass @hide
31805      */
31806     /**
31807      * @cfg {String} invalidText @hide
31808      */
31809     /**
31810      * @cfg {String} msgFx @hide
31811      */
31812     /**
31813      * @cfg {String} validateOnBlur @hide
31814      */
31815 });
31816
31817 Roo.HtmlEditorCore.white = [
31818         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31819         
31820        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31821        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31822        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31823        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31824        'TABLE',   'UL',         'XMP', 
31825        
31826        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31827       'THEAD',   'TR', 
31828      
31829       'DIR', 'MENU', 'OL', 'UL', 'DL',
31830        
31831       'EMBED',  'OBJECT'
31832 ];
31833
31834
31835 Roo.HtmlEditorCore.black = [
31836     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31837         'APPLET', // 
31838         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31839         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31840         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31841         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31842         //'FONT' // CLEAN LATER..
31843         'COLGROUP', 'COL'   // messy tables.
31844         
31845         
31846 ];
31847 Roo.HtmlEditorCore.clean = [ // ?? needed???
31848      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31849 ];
31850 Roo.HtmlEditorCore.tag_remove = [
31851     'FONT', 'TBODY'  
31852 ];
31853 // attributes..
31854
31855 Roo.HtmlEditorCore.ablack = [
31856     'on'
31857 ];
31858     
31859 Roo.HtmlEditorCore.aclean = [ 
31860     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31861 ];
31862
31863 // protocols..
31864 Roo.HtmlEditorCore.pwhite= [
31865         'http',  'https',  'mailto'
31866 ];
31867
31868 // white listed style attributes.
31869 Roo.HtmlEditorCore.cwhite= [
31870       //  'text-align', /// default is to allow most things..
31871       
31872          
31873 //        'font-size'//??
31874 ];
31875
31876 // black listed style attributes.
31877 Roo.HtmlEditorCore.cblack= [
31878       //  'font-size' -- this can be set by the project 
31879 ];
31880
31881
31882
31883
31884     /*
31885  * - LGPL
31886  *
31887  * HtmlEditor
31888  * 
31889  */
31890
31891 /**
31892  * @class Roo.bootstrap.form.HtmlEditor
31893  * @extends Roo.bootstrap.form.TextArea
31894  * Bootstrap HtmlEditor class
31895
31896  * @constructor
31897  * Create a new HtmlEditor
31898  * @param {Object} config The config object
31899  */
31900
31901 Roo.bootstrap.form.HtmlEditor = function(config){
31902     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31903     if (!this.toolbars) {
31904         this.toolbars = [];
31905     }
31906     
31907     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31908     this.addEvents({
31909             /**
31910              * @event initialize
31911              * Fires when the editor is fully initialized (including the iframe)
31912              * @param {HtmlEditor} this
31913              */
31914             initialize: true,
31915             /**
31916              * @event activate
31917              * Fires when the editor is first receives the focus. Any insertion must wait
31918              * until after this event.
31919              * @param {HtmlEditor} this
31920              */
31921             activate: true,
31922              /**
31923              * @event beforesync
31924              * Fires before the textarea is updated with content from the editor iframe. Return false
31925              * to cancel the sync.
31926              * @param {HtmlEditor} this
31927              * @param {String} html
31928              */
31929             beforesync: true,
31930              /**
31931              * @event beforepush
31932              * Fires before the iframe editor is updated with content from the textarea. Return false
31933              * to cancel the push.
31934              * @param {HtmlEditor} this
31935              * @param {String} html
31936              */
31937             beforepush: true,
31938              /**
31939              * @event sync
31940              * Fires when the textarea is updated with content from the editor iframe.
31941              * @param {HtmlEditor} this
31942              * @param {String} html
31943              */
31944             sync: true,
31945              /**
31946              * @event push
31947              * Fires when the iframe editor is updated with content from the textarea.
31948              * @param {HtmlEditor} this
31949              * @param {String} html
31950              */
31951             push: true,
31952              /**
31953              * @event editmodechange
31954              * Fires when the editor switches edit modes
31955              * @param {HtmlEditor} this
31956              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31957              */
31958             editmodechange: true,
31959             /**
31960              * @event editorevent
31961              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31962              * @param {HtmlEditor} this
31963              */
31964             editorevent: true,
31965             /**
31966              * @event firstfocus
31967              * Fires when on first focus - needed by toolbars..
31968              * @param {HtmlEditor} this
31969              */
31970             firstfocus: true,
31971             /**
31972              * @event autosave
31973              * Auto save the htmlEditor value as a file into Events
31974              * @param {HtmlEditor} this
31975              */
31976             autosave: true,
31977             /**
31978              * @event savedpreview
31979              * preview the saved version of htmlEditor
31980              * @param {HtmlEditor} this
31981              */
31982             savedpreview: true
31983         });
31984 };
31985
31986
31987 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
31988     
31989     
31990       /**
31991      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31992      */
31993     toolbars : false,
31994     
31995      /**
31996     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31997     */
31998     btns : [],
31999    
32000      /**
32001      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32002      *                        Roo.resizable.
32003      */
32004     resizable : false,
32005      /**
32006      * @cfg {Number} height (in pixels)
32007      */   
32008     height: 300,
32009    /**
32010      * @cfg {Number} width (in pixels)
32011      */   
32012     width: false,
32013     
32014     /**
32015      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32016      * 
32017      */
32018     stylesheets: false,
32019     
32020     // id of frame..
32021     frameId: false,
32022     
32023     // private properties
32024     validationEvent : false,
32025     deferHeight: true,
32026     initialized : false,
32027     activated : false,
32028     
32029     onFocus : Roo.emptyFn,
32030     iframePad:3,
32031     hideMode:'offsets',
32032     
32033     tbContainer : false,
32034     
32035     bodyCls : '',
32036     
32037     toolbarContainer :function() {
32038         return this.wrap.select('.x-html-editor-tb',true).first();
32039     },
32040
32041     /**
32042      * Protected method that will not generally be called directly. It
32043      * is called when the editor creates its toolbar. Override this method if you need to
32044      * add custom toolbar buttons.
32045      * @param {HtmlEditor} editor
32046      */
32047     createToolbar : function(){
32048         Roo.log('renewing');
32049         Roo.log("create toolbars");
32050         
32051         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32052         this.toolbars[0].render(this.toolbarContainer());
32053         
32054         return;
32055         
32056 //        if (!editor.toolbars || !editor.toolbars.length) {
32057 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32058 //        }
32059 //        
32060 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32061 //            editor.toolbars[i] = Roo.factory(
32062 //                    typeof(editor.toolbars[i]) == 'string' ?
32063 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32064 //                Roo.bootstrap.form.HtmlEditor);
32065 //            editor.toolbars[i].init(editor);
32066 //        }
32067     },
32068
32069      
32070     // private
32071     onRender : function(ct, position)
32072     {
32073        // Roo.log("Call onRender: " + this.xtype);
32074         var _t = this;
32075         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32076       
32077         this.wrap = this.inputEl().wrap({
32078             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32079         });
32080         
32081         this.editorcore.onRender(ct, position);
32082          
32083         if (this.resizable) {
32084             this.resizeEl = new Roo.Resizable(this.wrap, {
32085                 pinned : true,
32086                 wrap: true,
32087                 dynamic : true,
32088                 minHeight : this.height,
32089                 height: this.height,
32090                 handles : this.resizable,
32091                 width: this.width,
32092                 listeners : {
32093                     resize : function(r, w, h) {
32094                         _t.onResize(w,h); // -something
32095                     }
32096                 }
32097             });
32098             
32099         }
32100         this.createToolbar(this);
32101        
32102         
32103         if(!this.width && this.resizable){
32104             this.setSize(this.wrap.getSize());
32105         }
32106         if (this.resizeEl) {
32107             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32108             // should trigger onReize..
32109         }
32110         
32111     },
32112
32113     // private
32114     onResize : function(w, h)
32115     {
32116         Roo.log('resize: ' +w + ',' + h );
32117         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32118         var ew = false;
32119         var eh = false;
32120         
32121         if(this.inputEl() ){
32122             if(typeof w == 'number'){
32123                 var aw = w - this.wrap.getFrameWidth('lr');
32124                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32125                 ew = aw;
32126             }
32127             if(typeof h == 'number'){
32128                  var tbh = -11;  // fixme it needs to tool bar size!
32129                 for (var i =0; i < this.toolbars.length;i++) {
32130                     // fixme - ask toolbars for heights?
32131                     tbh += this.toolbars[i].el.getHeight();
32132                     //if (this.toolbars[i].footer) {
32133                     //    tbh += this.toolbars[i].footer.el.getHeight();
32134                     //}
32135                 }
32136               
32137                 
32138                 
32139                 
32140                 
32141                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32142                 ah -= 5; // knock a few pixes off for look..
32143                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32144                 var eh = ah;
32145             }
32146         }
32147         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32148         this.editorcore.onResize(ew,eh);
32149         
32150     },
32151
32152     /**
32153      * Toggles the editor between standard and source edit mode.
32154      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32155      */
32156     toggleSourceEdit : function(sourceEditMode)
32157     {
32158         this.editorcore.toggleSourceEdit(sourceEditMode);
32159         
32160         if(this.editorcore.sourceEditMode){
32161             Roo.log('editor - showing textarea');
32162             
32163 //            Roo.log('in');
32164 //            Roo.log(this.syncValue());
32165             this.syncValue();
32166             this.inputEl().removeClass(['hide', 'x-hidden']);
32167             this.inputEl().dom.removeAttribute('tabIndex');
32168             this.inputEl().focus();
32169         }else{
32170             Roo.log('editor - hiding textarea');
32171 //            Roo.log('out')
32172 //            Roo.log(this.pushValue()); 
32173             this.pushValue();
32174             
32175             this.inputEl().addClass(['hide', 'x-hidden']);
32176             this.inputEl().dom.setAttribute('tabIndex', -1);
32177             //this.deferFocus();
32178         }
32179          
32180         if(this.resizable){
32181             this.setSize(this.wrap.getSize());
32182         }
32183         
32184         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32185     },
32186  
32187     // private (for BoxComponent)
32188     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32189
32190     // private (for BoxComponent)
32191     getResizeEl : function(){
32192         return this.wrap;
32193     },
32194
32195     // private (for BoxComponent)
32196     getPositionEl : function(){
32197         return this.wrap;
32198     },
32199
32200     // private
32201     initEvents : function(){
32202         this.originalValue = this.getValue();
32203     },
32204
32205 //    /**
32206 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32207 //     * @method
32208 //     */
32209 //    markInvalid : Roo.emptyFn,
32210 //    /**
32211 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32212 //     * @method
32213 //     */
32214 //    clearInvalid : Roo.emptyFn,
32215
32216     setValue : function(v){
32217         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32218         this.editorcore.pushValue();
32219     },
32220
32221      
32222     // private
32223     deferFocus : function(){
32224         this.focus.defer(10, this);
32225     },
32226
32227     // doc'ed in Field
32228     focus : function(){
32229         this.editorcore.focus();
32230         
32231     },
32232       
32233
32234     // private
32235     onDestroy : function(){
32236         
32237         
32238         
32239         if(this.rendered){
32240             
32241             for (var i =0; i < this.toolbars.length;i++) {
32242                 // fixme - ask toolbars for heights?
32243                 this.toolbars[i].onDestroy();
32244             }
32245             
32246             this.wrap.dom.innerHTML = '';
32247             this.wrap.remove();
32248         }
32249     },
32250
32251     // private
32252     onFirstFocus : function(){
32253         //Roo.log("onFirstFocus");
32254         this.editorcore.onFirstFocus();
32255          for (var i =0; i < this.toolbars.length;i++) {
32256             this.toolbars[i].onFirstFocus();
32257         }
32258         
32259     },
32260     
32261     // private
32262     syncValue : function()
32263     {   
32264         this.editorcore.syncValue();
32265     },
32266     
32267     pushValue : function()
32268     {   
32269         this.editorcore.pushValue();
32270     }
32271      
32272     
32273     // hide stuff that is not compatible
32274     /**
32275      * @event blur
32276      * @hide
32277      */
32278     /**
32279      * @event change
32280      * @hide
32281      */
32282     /**
32283      * @event focus
32284      * @hide
32285      */
32286     /**
32287      * @event specialkey
32288      * @hide
32289      */
32290     /**
32291      * @cfg {String} fieldClass @hide
32292      */
32293     /**
32294      * @cfg {String} focusClass @hide
32295      */
32296     /**
32297      * @cfg {String} autoCreate @hide
32298      */
32299     /**
32300      * @cfg {String} inputType @hide
32301      */
32302      
32303     /**
32304      * @cfg {String} invalidText @hide
32305      */
32306     /**
32307      * @cfg {String} msgFx @hide
32308      */
32309     /**
32310      * @cfg {String} validateOnBlur @hide
32311      */
32312 });
32313  
32314     
32315    
32316    
32317    
32318       
32319 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32320 /**
32321  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32322  * @parent Roo.bootstrap.form.HtmlEditor
32323  * @extends Roo.bootstrap.nav.Simplebar
32324  * Basic Toolbar
32325  * 
32326  * @example
32327  * Usage:
32328  *
32329  new Roo.bootstrap.form.HtmlEditor({
32330     ....
32331     toolbars : [
32332         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32333             disable : { fonts: 1 , format: 1, ..., ... , ...],
32334             btns : [ .... ]
32335         })
32336     }
32337      
32338  * 
32339  * @cfg {Object} disable List of elements to disable..
32340  * @cfg {Array} btns List of additional buttons.
32341  * 
32342  * 
32343  * NEEDS Extra CSS? 
32344  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32345  */
32346  
32347 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32348 {
32349     
32350     Roo.apply(this, config);
32351     
32352     // default disabled, based on 'good practice'..
32353     this.disable = this.disable || {};
32354     Roo.applyIf(this.disable, {
32355         fontSize : true,
32356         colors : true,
32357         specialElements : true
32358     });
32359     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32360     
32361     this.editor = config.editor;
32362     this.editorcore = config.editor.editorcore;
32363     
32364     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32365     
32366     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32367     // dont call parent... till later.
32368 }
32369 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32370      
32371     bar : true,
32372     
32373     editor : false,
32374     editorcore : false,
32375     
32376     
32377     formats : [
32378         "p" ,  
32379         "h1","h2","h3","h4","h5","h6", 
32380         "pre", "code", 
32381         "abbr", "acronym", "address", "cite", "samp", "var",
32382         'div','span'
32383     ],
32384     
32385     onRender : function(ct, position)
32386     {
32387        // Roo.log("Call onRender: " + this.xtype);
32388         
32389        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32390        Roo.log(this.el);
32391        this.el.dom.style.marginBottom = '0';
32392        var _this = this;
32393        var editorcore = this.editorcore;
32394        var editor= this.editor;
32395        
32396        var children = [];
32397        var btn = function(id,cmd , toggle, handler, html){
32398        
32399             var  event = toggle ? 'toggle' : 'click';
32400        
32401             var a = {
32402                 size : 'sm',
32403                 xtype: 'Button',
32404                 xns: Roo.bootstrap,
32405                 //glyphicon : id,
32406                 fa: id,
32407                 cmd : id || cmd,
32408                 enableToggle:toggle !== false,
32409                 html : html || '',
32410                 pressed : toggle ? false : null,
32411                 listeners : {}
32412             };
32413             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32414                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32415             };
32416             children.push(a);
32417             return a;
32418        }
32419        
32420     //    var cb_box = function...
32421         
32422         var style = {
32423                 xtype: 'Button',
32424                 size : 'sm',
32425                 xns: Roo.bootstrap,
32426                 fa : 'font',
32427                 //html : 'submit'
32428                 menu : {
32429                     xtype: 'Menu',
32430                     xns: Roo.bootstrap,
32431                     items:  []
32432                 }
32433         };
32434         Roo.each(this.formats, function(f) {
32435             style.menu.items.push({
32436                 xtype :'MenuItem',
32437                 xns: Roo.bootstrap,
32438                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32439                 tagname : f,
32440                 listeners : {
32441                     click : function()
32442                     {
32443                         editorcore.insertTag(this.tagname);
32444                         editor.focus();
32445                     }
32446                 }
32447                 
32448             });
32449         });
32450         children.push(style);   
32451         
32452         btn('bold',false,true);
32453         btn('italic',false,true);
32454         btn('align-left', 'justifyleft',true);
32455         btn('align-center', 'justifycenter',true);
32456         btn('align-right' , 'justifyright',true);
32457         btn('link', false, false, function(btn) {
32458             //Roo.log("create link?");
32459             var url = prompt(this.createLinkText, this.defaultLinkValue);
32460             if(url && url != 'http:/'+'/'){
32461                 this.editorcore.relayCmd('createlink', url);
32462             }
32463         }),
32464         btn('list','insertunorderedlist',true);
32465         btn('pencil', false,true, function(btn){
32466                 Roo.log(this);
32467                 this.toggleSourceEdit(btn.pressed);
32468         });
32469         
32470         if (this.editor.btns.length > 0) {
32471             for (var i = 0; i<this.editor.btns.length; i++) {
32472                 children.push(this.editor.btns[i]);
32473             }
32474         }
32475         
32476         /*
32477         var cog = {
32478                 xtype: 'Button',
32479                 size : 'sm',
32480                 xns: Roo.bootstrap,
32481                 glyphicon : 'cog',
32482                 //html : 'submit'
32483                 menu : {
32484                     xtype: 'Menu',
32485                     xns: Roo.bootstrap,
32486                     items:  []
32487                 }
32488         };
32489         
32490         cog.menu.items.push({
32491             xtype :'MenuItem',
32492             xns: Roo.bootstrap,
32493             html : Clean styles,
32494             tagname : f,
32495             listeners : {
32496                 click : function()
32497                 {
32498                     editorcore.insertTag(this.tagname);
32499                     editor.focus();
32500                 }
32501             }
32502             
32503         });
32504        */
32505         
32506          
32507        this.xtype = 'NavSimplebar';
32508         
32509         for(var i=0;i< children.length;i++) {
32510             
32511             this.buttons.add(this.addxtypeChild(children[i]));
32512             
32513         }
32514         
32515         editor.on('editorevent', this.updateToolbar, this);
32516     },
32517     onBtnClick : function(id)
32518     {
32519        this.editorcore.relayCmd(id);
32520        this.editorcore.focus();
32521     },
32522     
32523     /**
32524      * Protected method that will not generally be called directly. It triggers
32525      * a toolbar update by reading the markup state of the current selection in the editor.
32526      */
32527     updateToolbar: function(){
32528
32529         if(!this.editorcore.activated){
32530             this.editor.onFirstFocus(); // is this neeed?
32531             return;
32532         }
32533
32534         var btns = this.buttons; 
32535         var doc = this.editorcore.doc;
32536         btns.get('bold').setActive(doc.queryCommandState('bold'));
32537         btns.get('italic').setActive(doc.queryCommandState('italic'));
32538         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32539         
32540         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32541         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32542         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32543         
32544         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32545         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32546          /*
32547         
32548         var ans = this.editorcore.getAllAncestors();
32549         if (this.formatCombo) {
32550             
32551             
32552             var store = this.formatCombo.store;
32553             this.formatCombo.setValue("");
32554             for (var i =0; i < ans.length;i++) {
32555                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32556                     // select it..
32557                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32558                     break;
32559                 }
32560             }
32561         }
32562         
32563         
32564         
32565         // hides menus... - so this cant be on a menu...
32566         Roo.bootstrap.MenuMgr.hideAll();
32567         */
32568         Roo.bootstrap.menu.Manager.hideAll();
32569         //this.editorsyncValue();
32570     },
32571     onFirstFocus: function() {
32572         this.buttons.each(function(item){
32573            item.enable();
32574         });
32575     },
32576     toggleSourceEdit : function(sourceEditMode){
32577         
32578           
32579         if(sourceEditMode){
32580             Roo.log("disabling buttons");
32581            this.buttons.each( function(item){
32582                 if(item.cmd != 'pencil'){
32583                     item.disable();
32584                 }
32585             });
32586           
32587         }else{
32588             Roo.log("enabling buttons");
32589             if(this.editorcore.initialized){
32590                 this.buttons.each( function(item){
32591                     item.enable();
32592                 });
32593             }
32594             
32595         }
32596         Roo.log("calling toggole on editor");
32597         // tell the editor that it's been pressed..
32598         this.editor.toggleSourceEdit(sourceEditMode);
32599        
32600     }
32601 });
32602
32603
32604
32605
32606  
32607 /*
32608  * - LGPL
32609  */
32610
32611 /**
32612  * @class Roo.bootstrap.form.Markdown
32613  * @extends Roo.bootstrap.form.TextArea
32614  * Bootstrap Showdown editable area
32615  * @cfg {string} content
32616  * 
32617  * @constructor
32618  * Create a new Showdown
32619  */
32620
32621 Roo.bootstrap.form.Markdown = function(config){
32622     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32623    
32624 };
32625
32626 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32627     
32628     editing :false,
32629     
32630     initEvents : function()
32631     {
32632         
32633         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32634         this.markdownEl = this.el.createChild({
32635             cls : 'roo-markdown-area'
32636         });
32637         this.inputEl().addClass('d-none');
32638         if (this.getValue() == '') {
32639             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32640             
32641         } else {
32642             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32643         }
32644         this.markdownEl.on('click', this.toggleTextEdit, this);
32645         this.on('blur', this.toggleTextEdit, this);
32646         this.on('specialkey', this.resizeTextArea, this);
32647     },
32648     
32649     toggleTextEdit : function()
32650     {
32651         var sh = this.markdownEl.getHeight();
32652         this.inputEl().addClass('d-none');
32653         this.markdownEl.addClass('d-none');
32654         if (!this.editing) {
32655             // show editor?
32656             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32657             this.inputEl().removeClass('d-none');
32658             this.inputEl().focus();
32659             this.editing = true;
32660             return;
32661         }
32662         // show showdown...
32663         this.updateMarkdown();
32664         this.markdownEl.removeClass('d-none');
32665         this.editing = false;
32666         return;
32667     },
32668     updateMarkdown : function()
32669     {
32670         if (this.getValue() == '') {
32671             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32672             return;
32673         }
32674  
32675         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32676     },
32677     
32678     resizeTextArea: function () {
32679         
32680         var sh = 100;
32681         Roo.log([sh, this.getValue().split("\n").length * 30]);
32682         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32683     },
32684     setValue : function(val)
32685     {
32686         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32687         if (!this.editing) {
32688             this.updateMarkdown();
32689         }
32690         
32691     },
32692     focus : function()
32693     {
32694         if (!this.editing) {
32695             this.toggleTextEdit();
32696         }
32697         
32698     }
32699
32700
32701 });/*
32702  * Based on:
32703  * Ext JS Library 1.1.1
32704  * Copyright(c) 2006-2007, Ext JS, LLC.
32705  *
32706  * Originally Released Under LGPL - original licence link has changed is not relivant.
32707  *
32708  * Fork - LGPL
32709  * <script type="text/javascript">
32710  */
32711  
32712 /**
32713  * @class Roo.bootstrap.PagingToolbar
32714  * @extends Roo.bootstrap.nav.Simplebar
32715  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32716  * @constructor
32717  * Create a new PagingToolbar
32718  * @param {Object} config The config object
32719  * @param {Roo.data.Store} store
32720  */
32721 Roo.bootstrap.PagingToolbar = function(config)
32722 {
32723     // old args format still supported... - xtype is prefered..
32724         // created from xtype...
32725     
32726     this.ds = config.dataSource;
32727     
32728     if (config.store && !this.ds) {
32729         this.store= Roo.factory(config.store, Roo.data);
32730         this.ds = this.store;
32731         this.ds.xmodule = this.xmodule || false;
32732     }
32733     
32734     this.toolbarItems = [];
32735     if (config.items) {
32736         this.toolbarItems = config.items;
32737     }
32738     
32739     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32740     
32741     this.cursor = 0;
32742     
32743     if (this.ds) { 
32744         this.bind(this.ds);
32745     }
32746     
32747     if (Roo.bootstrap.version == 4) {
32748         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32749     } else {
32750         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32751     }
32752     
32753 };
32754
32755 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32756     /**
32757      * @cfg {Roo.bootstrap.Button} buttons[]
32758      * Buttons for the toolbar
32759      */
32760      /**
32761      * @cfg {Roo.data.Store} store
32762      * The underlying data store providing the paged data
32763      */
32764     /**
32765      * @cfg {String/HTMLElement/Element} container
32766      * container The id or element that will contain the toolbar
32767      */
32768     /**
32769      * @cfg {Boolean} displayInfo
32770      * True to display the displayMsg (defaults to false)
32771      */
32772     /**
32773      * @cfg {Number} pageSize
32774      * The number of records to display per page (defaults to 20)
32775      */
32776     pageSize: 20,
32777     /**
32778      * @cfg {String} displayMsg
32779      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32780      */
32781     displayMsg : 'Displaying {0} - {1} of {2}',
32782     /**
32783      * @cfg {String} emptyMsg
32784      * The message to display when no records are found (defaults to "No data to display")
32785      */
32786     emptyMsg : 'No data to display',
32787     /**
32788      * Customizable piece of the default paging text (defaults to "Page")
32789      * @type String
32790      */
32791     beforePageText : "Page",
32792     /**
32793      * Customizable piece of the default paging text (defaults to "of %0")
32794      * @type String
32795      */
32796     afterPageText : "of {0}",
32797     /**
32798      * Customizable piece of the default paging text (defaults to "First Page")
32799      * @type String
32800      */
32801     firstText : "First Page",
32802     /**
32803      * Customizable piece of the default paging text (defaults to "Previous Page")
32804      * @type String
32805      */
32806     prevText : "Previous Page",
32807     /**
32808      * Customizable piece of the default paging text (defaults to "Next Page")
32809      * @type String
32810      */
32811     nextText : "Next Page",
32812     /**
32813      * Customizable piece of the default paging text (defaults to "Last Page")
32814      * @type String
32815      */
32816     lastText : "Last Page",
32817     /**
32818      * Customizable piece of the default paging text (defaults to "Refresh")
32819      * @type String
32820      */
32821     refreshText : "Refresh",
32822
32823     buttons : false,
32824     // private
32825     onRender : function(ct, position) 
32826     {
32827         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32828         this.navgroup.parentId = this.id;
32829         this.navgroup.onRender(this.el, null);
32830         // add the buttons to the navgroup
32831         
32832         if(this.displayInfo){
32833             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32834             this.displayEl = this.el.select('.x-paging-info', true).first();
32835 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32836 //            this.displayEl = navel.el.select('span',true).first();
32837         }
32838         
32839         var _this = this;
32840         
32841         if(this.buttons){
32842             Roo.each(_this.buttons, function(e){ // this might need to use render????
32843                Roo.factory(e).render(_this.el);
32844             });
32845         }
32846             
32847         Roo.each(_this.toolbarItems, function(e) {
32848             _this.navgroup.addItem(e);
32849         });
32850         
32851         
32852         this.first = this.navgroup.addItem({
32853             tooltip: this.firstText,
32854             cls: "prev btn-outline-secondary",
32855             html : ' <i class="fa fa-step-backward"></i>',
32856             disabled: true,
32857             preventDefault: true,
32858             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32859         });
32860         
32861         this.prev =  this.navgroup.addItem({
32862             tooltip: this.prevText,
32863             cls: "prev btn-outline-secondary",
32864             html : ' <i class="fa fa-backward"></i>',
32865             disabled: true,
32866             preventDefault: true,
32867             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32868         });
32869     //this.addSeparator();
32870         
32871         
32872         var field = this.navgroup.addItem( {
32873             tagtype : 'span',
32874             cls : 'x-paging-position  btn-outline-secondary',
32875              disabled: true,
32876             html : this.beforePageText  +
32877                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32878                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32879          } ); //?? escaped?
32880         
32881         this.field = field.el.select('input', true).first();
32882         this.field.on("keydown", this.onPagingKeydown, this);
32883         this.field.on("focus", function(){this.dom.select();});
32884     
32885     
32886         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32887         //this.field.setHeight(18);
32888         //this.addSeparator();
32889         this.next = this.navgroup.addItem({
32890             tooltip: this.nextText,
32891             cls: "next btn-outline-secondary",
32892             html : ' <i class="fa fa-forward"></i>',
32893             disabled: true,
32894             preventDefault: true,
32895             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32896         });
32897         this.last = this.navgroup.addItem({
32898             tooltip: this.lastText,
32899             html : ' <i class="fa fa-step-forward"></i>',
32900             cls: "next btn-outline-secondary",
32901             disabled: true,
32902             preventDefault: true,
32903             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32904         });
32905     //this.addSeparator();
32906         this.loading = this.navgroup.addItem({
32907             tooltip: this.refreshText,
32908             cls: "btn-outline-secondary",
32909             html : ' <i class="fa fa-refresh"></i>',
32910             preventDefault: true,
32911             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32912         });
32913         
32914     },
32915
32916     // private
32917     updateInfo : function(){
32918         if(this.displayEl){
32919             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32920             var msg = count == 0 ?
32921                 this.emptyMsg :
32922                 String.format(
32923                     this.displayMsg,
32924                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32925                 );
32926             this.displayEl.update(msg);
32927         }
32928     },
32929
32930     // private
32931     onLoad : function(ds, r, o)
32932     {
32933         this.cursor = o.params && o.params.start ? o.params.start : 0;
32934         
32935         var d = this.getPageData(),
32936             ap = d.activePage,
32937             ps = d.pages;
32938         
32939         
32940         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32941         this.field.dom.value = ap;
32942         this.first.setDisabled(ap == 1);
32943         this.prev.setDisabled(ap == 1);
32944         this.next.setDisabled(ap == ps);
32945         this.last.setDisabled(ap == ps);
32946         this.loading.enable();
32947         this.updateInfo();
32948     },
32949
32950     // private
32951     getPageData : function(){
32952         var total = this.ds.getTotalCount();
32953         return {
32954             total : total,
32955             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32956             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32957         };
32958     },
32959
32960     // private
32961     onLoadError : function(proxy, o){
32962         this.loading.enable();
32963         if (this.ds.events.loadexception.listeners.length  < 2) {
32964             // nothing has been assigned to loadexception except this...
32965             // so 
32966             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32967
32968         }
32969     },
32970
32971     // private
32972     onPagingKeydown : function(e){
32973         var k = e.getKey();
32974         var d = this.getPageData();
32975         if(k == e.RETURN){
32976             var v = this.field.dom.value, pageNum;
32977             if(!v || isNaN(pageNum = parseInt(v, 10))){
32978                 this.field.dom.value = d.activePage;
32979                 return;
32980             }
32981             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32982             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32983             e.stopEvent();
32984         }
32985         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))
32986         {
32987           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32988           this.field.dom.value = pageNum;
32989           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32990           e.stopEvent();
32991         }
32992         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32993         {
32994           var v = this.field.dom.value, pageNum; 
32995           var increment = (e.shiftKey) ? 10 : 1;
32996           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32997                 increment *= -1;
32998           }
32999           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33000             this.field.dom.value = d.activePage;
33001             return;
33002           }
33003           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33004           {
33005             this.field.dom.value = parseInt(v, 10) + increment;
33006             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33007             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33008           }
33009           e.stopEvent();
33010         }
33011     },
33012
33013     // private
33014     beforeLoad : function(){
33015         if(this.loading){
33016             this.loading.disable();
33017         }
33018     },
33019
33020     // private
33021     onClick : function(which){
33022         
33023         var ds = this.ds;
33024         if (!ds) {
33025             return;
33026         }
33027         
33028         switch(which){
33029             case "first":
33030                 ds.load({params:{start: 0, limit: this.pageSize}});
33031             break;
33032             case "prev":
33033                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33034             break;
33035             case "next":
33036                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33037             break;
33038             case "last":
33039                 var total = ds.getTotalCount();
33040                 var extra = total % this.pageSize;
33041                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33042                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33043             break;
33044             case "refresh":
33045                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33046             break;
33047         }
33048     },
33049
33050     /**
33051      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33052      * @param {Roo.data.Store} store The data store to unbind
33053      */
33054     unbind : function(ds){
33055         ds.un("beforeload", this.beforeLoad, this);
33056         ds.un("load", this.onLoad, this);
33057         ds.un("loadexception", this.onLoadError, this);
33058         ds.un("remove", this.updateInfo, this);
33059         ds.un("add", this.updateInfo, this);
33060         this.ds = undefined;
33061     },
33062
33063     /**
33064      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33065      * @param {Roo.data.Store} store The data store to bind
33066      */
33067     bind : function(ds){
33068         ds.on("beforeload", this.beforeLoad, this);
33069         ds.on("load", this.onLoad, this);
33070         ds.on("loadexception", this.onLoadError, this);
33071         ds.on("remove", this.updateInfo, this);
33072         ds.on("add", this.updateInfo, this);
33073         this.ds = ds;
33074     }
33075 });/*
33076  * - LGPL
33077  *
33078  * element
33079  * 
33080  */
33081
33082 /**
33083  * @class Roo.bootstrap.MessageBar
33084  * @extends Roo.bootstrap.Component
33085  * Bootstrap MessageBar class
33086  * @cfg {String} html contents of the MessageBar
33087  * @cfg {String} weight (info | success | warning | danger) default info
33088  * @cfg {String} beforeClass insert the bar before the given class
33089  * @cfg {Boolean} closable (true | false) default false
33090  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33091  * 
33092  * @constructor
33093  * Create a new Element
33094  * @param {Object} config The config object
33095  */
33096
33097 Roo.bootstrap.MessageBar = function(config){
33098     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33099 };
33100
33101 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33102     
33103     html: '',
33104     weight: 'info',
33105     closable: false,
33106     fixed: false,
33107     beforeClass: 'bootstrap-sticky-wrap',
33108     
33109     getAutoCreate : function(){
33110         
33111         var cfg = {
33112             tag: 'div',
33113             cls: 'alert alert-dismissable alert-' + this.weight,
33114             cn: [
33115                 {
33116                     tag: 'span',
33117                     cls: 'message',
33118                     html: this.html || ''
33119                 }
33120             ]
33121         };
33122         
33123         if(this.fixed){
33124             cfg.cls += ' alert-messages-fixed';
33125         }
33126         
33127         if(this.closable){
33128             cfg.cn.push({
33129                 tag: 'button',
33130                 cls: 'close',
33131                 html: 'x'
33132             });
33133         }
33134         
33135         return cfg;
33136     },
33137     
33138     onRender : function(ct, position)
33139     {
33140         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33141         
33142         if(!this.el){
33143             var cfg = Roo.apply({},  this.getAutoCreate());
33144             cfg.id = Roo.id();
33145             
33146             if (this.cls) {
33147                 cfg.cls += ' ' + this.cls;
33148             }
33149             if (this.style) {
33150                 cfg.style = this.style;
33151             }
33152             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33153             
33154             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33155         }
33156         
33157         this.el.select('>button.close').on('click', this.hide, this);
33158         
33159     },
33160     
33161     show : function()
33162     {
33163         if (!this.rendered) {
33164             this.render();
33165         }
33166         
33167         this.el.show();
33168         
33169         this.fireEvent('show', this);
33170         
33171     },
33172     
33173     hide : function()
33174     {
33175         if (!this.rendered) {
33176             this.render();
33177         }
33178         
33179         this.el.hide();
33180         
33181         this.fireEvent('hide', this);
33182     },
33183     
33184     update : function()
33185     {
33186 //        var e = this.el.dom.firstChild;
33187 //        
33188 //        if(this.closable){
33189 //            e = e.nextSibling;
33190 //        }
33191 //        
33192 //        e.data = this.html || '';
33193
33194         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33195     }
33196    
33197 });
33198
33199  
33200
33201      /*
33202  * - LGPL
33203  *
33204  * Graph
33205  * 
33206  */
33207
33208
33209 /**
33210  * @class Roo.bootstrap.Graph
33211  * @extends Roo.bootstrap.Component
33212  * Bootstrap Graph class
33213 > Prameters
33214  -sm {number} sm 4
33215  -md {number} md 5
33216  @cfg {String} graphtype  bar | vbar | pie
33217  @cfg {number} g_x coodinator | centre x (pie)
33218  @cfg {number} g_y coodinator | centre y (pie)
33219  @cfg {number} g_r radius (pie)
33220  @cfg {number} g_height height of the chart (respected by all elements in the set)
33221  @cfg {number} g_width width of the chart (respected by all elements in the set)
33222  @cfg {Object} title The title of the chart
33223     
33224  -{Array}  values
33225  -opts (object) options for the chart 
33226      o {
33227      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33228      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33229      o vgutter (number)
33230      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.
33231      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33232      o to
33233      o stretch (boolean)
33234      o }
33235  -opts (object) options for the pie
33236      o{
33237      o cut
33238      o startAngle (number)
33239      o endAngle (number)
33240      } 
33241  *
33242  * @constructor
33243  * Create a new Input
33244  * @param {Object} config The config object
33245  */
33246
33247 Roo.bootstrap.Graph = function(config){
33248     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33249     
33250     this.addEvents({
33251         // img events
33252         /**
33253          * @event click
33254          * The img click event for the img.
33255          * @param {Roo.EventObject} e
33256          */
33257         "click" : true
33258     });
33259 };
33260
33261 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33262     
33263     sm: 4,
33264     md: 5,
33265     graphtype: 'bar',
33266     g_height: 250,
33267     g_width: 400,
33268     g_x: 50,
33269     g_y: 50,
33270     g_r: 30,
33271     opts:{
33272         //g_colors: this.colors,
33273         g_type: 'soft',
33274         g_gutter: '20%'
33275
33276     },
33277     title : false,
33278
33279     getAutoCreate : function(){
33280         
33281         var cfg = {
33282             tag: 'div',
33283             html : null
33284         };
33285         
33286         
33287         return  cfg;
33288     },
33289
33290     onRender : function(ct,position){
33291         
33292         
33293         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33294         
33295         if (typeof(Raphael) == 'undefined') {
33296             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33297             return;
33298         }
33299         
33300         this.raphael = Raphael(this.el.dom);
33301         
33302                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33303                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33304                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33305                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33306                 /*
33307                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33308                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33309                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33310                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33311                 
33312                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33313                 r.barchart(330, 10, 300, 220, data1);
33314                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33315                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33316                 */
33317                 
33318                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33319                 // r.barchart(30, 30, 560, 250,  xdata, {
33320                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33321                 //     axis : "0 0 1 1",
33322                 //     axisxlabels :  xdata
33323                 //     //yvalues : cols,
33324                    
33325                 // });
33326 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33327 //        
33328 //        this.load(null,xdata,{
33329 //                axis : "0 0 1 1",
33330 //                axisxlabels :  xdata
33331 //                });
33332
33333     },
33334
33335     load : function(graphtype,xdata,opts)
33336     {
33337         this.raphael.clear();
33338         if(!graphtype) {
33339             graphtype = this.graphtype;
33340         }
33341         if(!opts){
33342             opts = this.opts;
33343         }
33344         var r = this.raphael,
33345             fin = function () {
33346                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33347             },
33348             fout = function () {
33349                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33350             },
33351             pfin = function() {
33352                 this.sector.stop();
33353                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33354
33355                 if (this.label) {
33356                     this.label[0].stop();
33357                     this.label[0].attr({ r: 7.5 });
33358                     this.label[1].attr({ "font-weight": 800 });
33359                 }
33360             },
33361             pfout = function() {
33362                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33363
33364                 if (this.label) {
33365                     this.label[0].animate({ r: 5 }, 500, "bounce");
33366                     this.label[1].attr({ "font-weight": 400 });
33367                 }
33368             };
33369
33370         switch(graphtype){
33371             case 'bar':
33372                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33373                 break;
33374             case 'hbar':
33375                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33376                 break;
33377             case 'pie':
33378 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33379 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33380 //            
33381                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33382                 
33383                 break;
33384
33385         }
33386         
33387         if(this.title){
33388             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33389         }
33390         
33391     },
33392     
33393     setTitle: function(o)
33394     {
33395         this.title = o;
33396     },
33397     
33398     initEvents: function() {
33399         
33400         if(!this.href){
33401             this.el.on('click', this.onClick, this);
33402         }
33403     },
33404     
33405     onClick : function(e)
33406     {
33407         Roo.log('img onclick');
33408         this.fireEvent('click', this, e);
33409     }
33410    
33411 });
33412
33413  
33414 Roo.bootstrap.dash = {};/*
33415  * - LGPL
33416  *
33417  * numberBox
33418  * 
33419  */
33420 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33421
33422 /**
33423  * @class Roo.bootstrap.dash.NumberBox
33424  * @extends Roo.bootstrap.Component
33425  * Bootstrap NumberBox class
33426  * @cfg {String} headline Box headline
33427  * @cfg {String} content Box content
33428  * @cfg {String} icon Box icon
33429  * @cfg {String} footer Footer text
33430  * @cfg {String} fhref Footer href
33431  * 
33432  * @constructor
33433  * Create a new NumberBox
33434  * @param {Object} config The config object
33435  */
33436
33437
33438 Roo.bootstrap.dash.NumberBox = function(config){
33439     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33440     
33441 };
33442
33443 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33444     
33445     headline : '',
33446     content : '',
33447     icon : '',
33448     footer : '',
33449     fhref : '',
33450     ficon : '',
33451     
33452     getAutoCreate : function(){
33453         
33454         var cfg = {
33455             tag : 'div',
33456             cls : 'small-box ',
33457             cn : [
33458                 {
33459                     tag : 'div',
33460                     cls : 'inner',
33461                     cn :[
33462                         {
33463                             tag : 'h3',
33464                             cls : 'roo-headline',
33465                             html : this.headline
33466                         },
33467                         {
33468                             tag : 'p',
33469                             cls : 'roo-content',
33470                             html : this.content
33471                         }
33472                     ]
33473                 }
33474             ]
33475         };
33476         
33477         if(this.icon){
33478             cfg.cn.push({
33479                 tag : 'div',
33480                 cls : 'icon',
33481                 cn :[
33482                     {
33483                         tag : 'i',
33484                         cls : 'ion ' + this.icon
33485                     }
33486                 ]
33487             });
33488         }
33489         
33490         if(this.footer){
33491             var footer = {
33492                 tag : 'a',
33493                 cls : 'small-box-footer',
33494                 href : this.fhref || '#',
33495                 html : this.footer
33496             };
33497             
33498             cfg.cn.push(footer);
33499             
33500         }
33501         
33502         return  cfg;
33503     },
33504
33505     onRender : function(ct,position){
33506         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33507
33508
33509        
33510                 
33511     },
33512
33513     setHeadline: function (value)
33514     {
33515         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33516     },
33517     
33518     setFooter: function (value, href)
33519     {
33520         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33521         
33522         if(href){
33523             this.el.select('a.small-box-footer',true).first().attr('href', href);
33524         }
33525         
33526     },
33527
33528     setContent: function (value)
33529     {
33530         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33531     },
33532
33533     initEvents: function() 
33534     {   
33535         
33536     }
33537     
33538 });
33539
33540  
33541 /*
33542  * - LGPL
33543  *
33544  * TabBox
33545  * 
33546  */
33547 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33548
33549 /**
33550  * @class Roo.bootstrap.dash.TabBox
33551  * @extends Roo.bootstrap.Component
33552  * @children Roo.bootstrap.dash.TabPane
33553  * Bootstrap TabBox class
33554  * @cfg {String} title Title of the TabBox
33555  * @cfg {String} icon Icon of the TabBox
33556  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33557  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33558  * 
33559  * @constructor
33560  * Create a new TabBox
33561  * @param {Object} config The config object
33562  */
33563
33564
33565 Roo.bootstrap.dash.TabBox = function(config){
33566     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33567     this.addEvents({
33568         // raw events
33569         /**
33570          * @event addpane
33571          * When a pane is added
33572          * @param {Roo.bootstrap.dash.TabPane} pane
33573          */
33574         "addpane" : true,
33575         /**
33576          * @event activatepane
33577          * When a pane is activated
33578          * @param {Roo.bootstrap.dash.TabPane} pane
33579          */
33580         "activatepane" : true
33581         
33582          
33583     });
33584     
33585     this.panes = [];
33586 };
33587
33588 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33589
33590     title : '',
33591     icon : false,
33592     showtabs : true,
33593     tabScrollable : false,
33594     
33595     getChildContainer : function()
33596     {
33597         return this.el.select('.tab-content', true).first();
33598     },
33599     
33600     getAutoCreate : function(){
33601         
33602         var header = {
33603             tag: 'li',
33604             cls: 'pull-left header',
33605             html: this.title,
33606             cn : []
33607         };
33608         
33609         if(this.icon){
33610             header.cn.push({
33611                 tag: 'i',
33612                 cls: 'fa ' + this.icon
33613             });
33614         }
33615         
33616         var h = {
33617             tag: 'ul',
33618             cls: 'nav nav-tabs pull-right',
33619             cn: [
33620                 header
33621             ]
33622         };
33623         
33624         if(this.tabScrollable){
33625             h = {
33626                 tag: 'div',
33627                 cls: 'tab-header',
33628                 cn: [
33629                     {
33630                         tag: 'ul',
33631                         cls: 'nav nav-tabs pull-right',
33632                         cn: [
33633                             header
33634                         ]
33635                     }
33636                 ]
33637             };
33638         }
33639         
33640         var cfg = {
33641             tag: 'div',
33642             cls: 'nav-tabs-custom',
33643             cn: [
33644                 h,
33645                 {
33646                     tag: 'div',
33647                     cls: 'tab-content no-padding',
33648                     cn: []
33649                 }
33650             ]
33651         };
33652
33653         return  cfg;
33654     },
33655     initEvents : function()
33656     {
33657         //Roo.log('add add pane handler');
33658         this.on('addpane', this.onAddPane, this);
33659     },
33660      /**
33661      * Updates the box title
33662      * @param {String} html to set the title to.
33663      */
33664     setTitle : function(value)
33665     {
33666         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33667     },
33668     onAddPane : function(pane)
33669     {
33670         this.panes.push(pane);
33671         //Roo.log('addpane');
33672         //Roo.log(pane);
33673         // tabs are rendere left to right..
33674         if(!this.showtabs){
33675             return;
33676         }
33677         
33678         var ctr = this.el.select('.nav-tabs', true).first();
33679          
33680          
33681         var existing = ctr.select('.nav-tab',true);
33682         var qty = existing.getCount();;
33683         
33684         
33685         var tab = ctr.createChild({
33686             tag : 'li',
33687             cls : 'nav-tab' + (qty ? '' : ' active'),
33688             cn : [
33689                 {
33690                     tag : 'a',
33691                     href:'#',
33692                     html : pane.title
33693                 }
33694             ]
33695         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33696         pane.tab = tab;
33697         
33698         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33699         if (!qty) {
33700             pane.el.addClass('active');
33701         }
33702         
33703                 
33704     },
33705     onTabClick : function(ev,un,ob,pane)
33706     {
33707         //Roo.log('tab - prev default');
33708         ev.preventDefault();
33709         
33710         
33711         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33712         pane.tab.addClass('active');
33713         //Roo.log(pane.title);
33714         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33715         // technically we should have a deactivate event.. but maybe add later.
33716         // and it should not de-activate the selected tab...
33717         this.fireEvent('activatepane', pane);
33718         pane.el.addClass('active');
33719         pane.fireEvent('activate');
33720         
33721         
33722     },
33723     
33724     getActivePane : function()
33725     {
33726         var r = false;
33727         Roo.each(this.panes, function(p) {
33728             if(p.el.hasClass('active')){
33729                 r = p;
33730                 return false;
33731             }
33732             
33733             return;
33734         });
33735         
33736         return r;
33737     }
33738     
33739     
33740 });
33741
33742  
33743 /*
33744  * - LGPL
33745  *
33746  * Tab pane
33747  * 
33748  */
33749 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33750 /**
33751  * @class Roo.bootstrap.TabPane
33752  * @extends Roo.bootstrap.Component
33753  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33754  * Bootstrap TabPane class
33755  * @cfg {Boolean} active (false | true) Default false
33756  * @cfg {String} title title of panel
33757
33758  * 
33759  * @constructor
33760  * Create a new TabPane
33761  * @param {Object} config The config object
33762  */
33763
33764 Roo.bootstrap.dash.TabPane = function(config){
33765     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33766     
33767     this.addEvents({
33768         // raw events
33769         /**
33770          * @event activate
33771          * When a pane is activated
33772          * @param {Roo.bootstrap.dash.TabPane} pane
33773          */
33774         "activate" : true
33775          
33776     });
33777 };
33778
33779 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33780     
33781     active : false,
33782     title : '',
33783     
33784     // the tabBox that this is attached to.
33785     tab : false,
33786      
33787     getAutoCreate : function() 
33788     {
33789         var cfg = {
33790             tag: 'div',
33791             cls: 'tab-pane'
33792         };
33793         
33794         if(this.active){
33795             cfg.cls += ' active';
33796         }
33797         
33798         return cfg;
33799     },
33800     initEvents  : function()
33801     {
33802         //Roo.log('trigger add pane handler');
33803         this.parent().fireEvent('addpane', this)
33804     },
33805     
33806      /**
33807      * Updates the tab title 
33808      * @param {String} html to set the title to.
33809      */
33810     setTitle: function(str)
33811     {
33812         if (!this.tab) {
33813             return;
33814         }
33815         this.title = str;
33816         this.tab.select('a', true).first().dom.innerHTML = str;
33817         
33818     }
33819     
33820     
33821     
33822 });
33823
33824  
33825
33826
33827  /*
33828  * - LGPL
33829  *
33830  * Tooltip
33831  * 
33832  */
33833
33834 /**
33835  * @class Roo.bootstrap.Tooltip
33836  * Bootstrap Tooltip class
33837  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33838  * to determine which dom element triggers the tooltip.
33839  * 
33840  * It needs to add support for additional attributes like tooltip-position
33841  * 
33842  * @constructor
33843  * Create a new Toolti
33844  * @param {Object} config The config object
33845  */
33846
33847 Roo.bootstrap.Tooltip = function(config){
33848     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33849     
33850     this.alignment = Roo.bootstrap.Tooltip.alignment;
33851     
33852     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33853         this.alignment = config.alignment;
33854     }
33855     
33856 };
33857
33858 Roo.apply(Roo.bootstrap.Tooltip, {
33859     /**
33860      * @function init initialize tooltip monitoring.
33861      * @static
33862      */
33863     currentEl : false,
33864     currentTip : false,
33865     currentRegion : false,
33866     
33867     //  init : delay?
33868     
33869     init : function()
33870     {
33871         Roo.get(document).on('mouseover', this.enter ,this);
33872         Roo.get(document).on('mouseout', this.leave, this);
33873          
33874         
33875         this.currentTip = new Roo.bootstrap.Tooltip();
33876     },
33877     
33878     enter : function(ev)
33879     {
33880         var dom = ev.getTarget();
33881         
33882         //Roo.log(['enter',dom]);
33883         var el = Roo.fly(dom);
33884         if (this.currentEl) {
33885             //Roo.log(dom);
33886             //Roo.log(this.currentEl);
33887             //Roo.log(this.currentEl.contains(dom));
33888             if (this.currentEl == el) {
33889                 return;
33890             }
33891             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33892                 return;
33893             }
33894
33895         }
33896         
33897         if (this.currentTip.el) {
33898             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33899         }    
33900         //Roo.log(ev);
33901         
33902         if(!el || el.dom == document){
33903             return;
33904         }
33905         
33906         var bindEl = el; 
33907         var pel = false;
33908         if (!el.attr('tooltip')) {
33909             pel = el.findParent("[tooltip]");
33910             if (pel) {
33911                 bindEl = Roo.get(pel);
33912             }
33913         }
33914         
33915        
33916         
33917         // you can not look for children, as if el is the body.. then everythign is the child..
33918         if (!pel && !el.attr('tooltip')) { //
33919             if (!el.select("[tooltip]").elements.length) {
33920                 return;
33921             }
33922             // is the mouse over this child...?
33923             bindEl = el.select("[tooltip]").first();
33924             var xy = ev.getXY();
33925             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33926                 //Roo.log("not in region.");
33927                 return;
33928             }
33929             //Roo.log("child element over..");
33930             
33931         }
33932         this.currentEl = el;
33933         this.currentTip.bind(bindEl);
33934         this.currentRegion = Roo.lib.Region.getRegion(dom);
33935         this.currentTip.enter();
33936         
33937     },
33938     leave : function(ev)
33939     {
33940         var dom = ev.getTarget();
33941         //Roo.log(['leave',dom]);
33942         if (!this.currentEl) {
33943             return;
33944         }
33945         
33946         
33947         if (dom != this.currentEl.dom) {
33948             return;
33949         }
33950         var xy = ev.getXY();
33951         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33952             return;
33953         }
33954         // only activate leave if mouse cursor is outside... bounding box..
33955         
33956         
33957         
33958         
33959         if (this.currentTip) {
33960             this.currentTip.leave();
33961         }
33962         //Roo.log('clear currentEl');
33963         this.currentEl = false;
33964         
33965         
33966     },
33967     alignment : {
33968         'left' : ['r-l', [-2,0], 'right'],
33969         'right' : ['l-r', [2,0], 'left'],
33970         'bottom' : ['t-b', [0,2], 'top'],
33971         'top' : [ 'b-t', [0,-2], 'bottom']
33972     }
33973     
33974 });
33975
33976
33977 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33978     
33979     
33980     bindEl : false,
33981     
33982     delay : null, // can be { show : 300 , hide: 500}
33983     
33984     timeout : null,
33985     
33986     hoverState : null, //???
33987     
33988     placement : 'bottom', 
33989     
33990     alignment : false,
33991     
33992     getAutoCreate : function(){
33993     
33994         var cfg = {
33995            cls : 'tooltip',   
33996            role : 'tooltip',
33997            cn : [
33998                 {
33999                     cls : 'tooltip-arrow arrow'
34000                 },
34001                 {
34002                     cls : 'tooltip-inner'
34003                 }
34004            ]
34005         };
34006         
34007         return cfg;
34008     },
34009     bind : function(el)
34010     {
34011         this.bindEl = el;
34012     },
34013     
34014     initEvents : function()
34015     {
34016         this.arrowEl = this.el.select('.arrow', true).first();
34017         this.innerEl = this.el.select('.tooltip-inner', true).first();
34018     },
34019     
34020     enter : function () {
34021        
34022         if (this.timeout != null) {
34023             clearTimeout(this.timeout);
34024         }
34025         
34026         this.hoverState = 'in';
34027          //Roo.log("enter - show");
34028         if (!this.delay || !this.delay.show) {
34029             this.show();
34030             return;
34031         }
34032         var _t = this;
34033         this.timeout = setTimeout(function () {
34034             if (_t.hoverState == 'in') {
34035                 _t.show();
34036             }
34037         }, this.delay.show);
34038     },
34039     leave : function()
34040     {
34041         clearTimeout(this.timeout);
34042     
34043         this.hoverState = 'out';
34044          if (!this.delay || !this.delay.hide) {
34045             this.hide();
34046             return;
34047         }
34048        
34049         var _t = this;
34050         this.timeout = setTimeout(function () {
34051             //Roo.log("leave - timeout");
34052             
34053             if (_t.hoverState == 'out') {
34054                 _t.hide();
34055                 Roo.bootstrap.Tooltip.currentEl = false;
34056             }
34057         }, delay);
34058     },
34059     
34060     show : function (msg)
34061     {
34062         if (!this.el) {
34063             this.render(document.body);
34064         }
34065         // set content.
34066         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34067         
34068         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34069         
34070         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34071         
34072         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34073                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34074
34075         if(this.bindEl.attr('tooltip-class')) {
34076             this.el.addClass(this.bindEl.attr('tooltip-class'));
34077         }
34078         
34079         var placement = typeof this.placement == 'function' ?
34080             this.placement.call(this, this.el, on_el) :
34081             this.placement;
34082         
34083         if(this.bindEl.attr('tooltip-placement')) {
34084             placement = this.bindEl.attr('tooltip-placement');
34085         }
34086             
34087         var autoToken = /\s?auto?\s?/i;
34088         var autoPlace = autoToken.test(placement);
34089         if (autoPlace) {
34090             placement = placement.replace(autoToken, '') || 'top';
34091         }
34092         
34093         //this.el.detach()
34094         //this.el.setXY([0,0]);
34095         this.el.show();
34096         //this.el.dom.style.display='block';
34097         
34098         //this.el.appendTo(on_el);
34099         
34100         var p = this.getPosition();
34101         var box = this.el.getBox();
34102         
34103         if (autoPlace) {
34104             // fixme..
34105         }
34106         
34107         var align = this.alignment[placement];
34108         
34109         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34110         
34111         if(placement == 'top' || placement == 'bottom'){
34112             if(xy[0] < 0){
34113                 placement = 'right';
34114             }
34115             
34116             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34117                 placement = 'left';
34118             }
34119             
34120             var scroll = Roo.select('body', true).first().getScroll();
34121             
34122             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34123                 placement = 'top';
34124             }
34125             
34126             align = this.alignment[placement];
34127             
34128             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34129             
34130         }
34131         
34132         var elems = document.getElementsByTagName('div');
34133         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34134         for (var i = 0; i < elems.length; i++) {
34135           var zindex = Number.parseInt(
34136                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34137                 10
34138           );
34139           if (zindex > highest) {
34140             highest = zindex;
34141           }
34142         }
34143         
34144         
34145         
34146         this.el.dom.style.zIndex = highest;
34147         
34148         this.el.alignTo(this.bindEl, align[0],align[1]);
34149         //var arrow = this.el.select('.arrow',true).first();
34150         //arrow.set(align[2], 
34151         
34152         this.el.addClass(placement);
34153         this.el.addClass("bs-tooltip-"+ placement);
34154         
34155         this.el.addClass('in fade show');
34156         
34157         this.hoverState = null;
34158         
34159         if (this.el.hasClass('fade')) {
34160             // fade it?
34161         }
34162         
34163         
34164         
34165         
34166         
34167     },
34168     hide : function()
34169     {
34170          
34171         if (!this.el) {
34172             return;
34173         }
34174         //this.el.setXY([0,0]);
34175         if(this.bindEl.attr('tooltip-class')) {
34176             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34177         }
34178         this.el.removeClass(['show', 'in']);
34179         //this.el.hide();
34180         
34181     }
34182     
34183 });
34184  
34185
34186  /*
34187  * - LGPL
34188  *
34189  * Location Picker
34190  * 
34191  */
34192
34193 /**
34194  * @class Roo.bootstrap.LocationPicker
34195  * @extends Roo.bootstrap.Component
34196  * Bootstrap LocationPicker class
34197  * @cfg {Number} latitude Position when init default 0
34198  * @cfg {Number} longitude Position when init default 0
34199  * @cfg {Number} zoom default 15
34200  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34201  * @cfg {Boolean} mapTypeControl default false
34202  * @cfg {Boolean} disableDoubleClickZoom default false
34203  * @cfg {Boolean} scrollwheel default true
34204  * @cfg {Boolean} streetViewControl default false
34205  * @cfg {Number} radius default 0
34206  * @cfg {String} locationName
34207  * @cfg {Boolean} draggable default true
34208  * @cfg {Boolean} enableAutocomplete default false
34209  * @cfg {Boolean} enableReverseGeocode default true
34210  * @cfg {String} markerTitle
34211  * 
34212  * @constructor
34213  * Create a new LocationPicker
34214  * @param {Object} config The config object
34215  */
34216
34217
34218 Roo.bootstrap.LocationPicker = function(config){
34219     
34220     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34221     
34222     this.addEvents({
34223         /**
34224          * @event initial
34225          * Fires when the picker initialized.
34226          * @param {Roo.bootstrap.LocationPicker} this
34227          * @param {Google Location} location
34228          */
34229         initial : true,
34230         /**
34231          * @event positionchanged
34232          * Fires when the picker position changed.
34233          * @param {Roo.bootstrap.LocationPicker} this
34234          * @param {Google Location} location
34235          */
34236         positionchanged : true,
34237         /**
34238          * @event resize
34239          * Fires when the map resize.
34240          * @param {Roo.bootstrap.LocationPicker} this
34241          */
34242         resize : true,
34243         /**
34244          * @event show
34245          * Fires when the map show.
34246          * @param {Roo.bootstrap.LocationPicker} this
34247          */
34248         show : true,
34249         /**
34250          * @event hide
34251          * Fires when the map hide.
34252          * @param {Roo.bootstrap.LocationPicker} this
34253          */
34254         hide : true,
34255         /**
34256          * @event mapClick
34257          * Fires when click the map.
34258          * @param {Roo.bootstrap.LocationPicker} this
34259          * @param {Map event} e
34260          */
34261         mapClick : true,
34262         /**
34263          * @event mapRightClick
34264          * Fires when right click the map.
34265          * @param {Roo.bootstrap.LocationPicker} this
34266          * @param {Map event} e
34267          */
34268         mapRightClick : true,
34269         /**
34270          * @event markerClick
34271          * Fires when click the marker.
34272          * @param {Roo.bootstrap.LocationPicker} this
34273          * @param {Map event} e
34274          */
34275         markerClick : true,
34276         /**
34277          * @event markerRightClick
34278          * Fires when right click the marker.
34279          * @param {Roo.bootstrap.LocationPicker} this
34280          * @param {Map event} e
34281          */
34282         markerRightClick : true,
34283         /**
34284          * @event OverlayViewDraw
34285          * Fires when OverlayView Draw
34286          * @param {Roo.bootstrap.LocationPicker} this
34287          */
34288         OverlayViewDraw : true,
34289         /**
34290          * @event OverlayViewOnAdd
34291          * Fires when OverlayView Draw
34292          * @param {Roo.bootstrap.LocationPicker} this
34293          */
34294         OverlayViewOnAdd : true,
34295         /**
34296          * @event OverlayViewOnRemove
34297          * Fires when OverlayView Draw
34298          * @param {Roo.bootstrap.LocationPicker} this
34299          */
34300         OverlayViewOnRemove : true,
34301         /**
34302          * @event OverlayViewShow
34303          * Fires when OverlayView Draw
34304          * @param {Roo.bootstrap.LocationPicker} this
34305          * @param {Pixel} cpx
34306          */
34307         OverlayViewShow : true,
34308         /**
34309          * @event OverlayViewHide
34310          * Fires when OverlayView Draw
34311          * @param {Roo.bootstrap.LocationPicker} this
34312          */
34313         OverlayViewHide : true,
34314         /**
34315          * @event loadexception
34316          * Fires when load google lib failed.
34317          * @param {Roo.bootstrap.LocationPicker} this
34318          */
34319         loadexception : true
34320     });
34321         
34322 };
34323
34324 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34325     
34326     gMapContext: false,
34327     
34328     latitude: 0,
34329     longitude: 0,
34330     zoom: 15,
34331     mapTypeId: false,
34332     mapTypeControl: false,
34333     disableDoubleClickZoom: false,
34334     scrollwheel: true,
34335     streetViewControl: false,
34336     radius: 0,
34337     locationName: '',
34338     draggable: true,
34339     enableAutocomplete: false,
34340     enableReverseGeocode: true,
34341     markerTitle: '',
34342     
34343     getAutoCreate: function()
34344     {
34345
34346         var cfg = {
34347             tag: 'div',
34348             cls: 'roo-location-picker'
34349         };
34350         
34351         return cfg
34352     },
34353     
34354     initEvents: function(ct, position)
34355     {       
34356         if(!this.el.getWidth() || this.isApplied()){
34357             return;
34358         }
34359         
34360         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34361         
34362         this.initial();
34363     },
34364     
34365     initial: function()
34366     {
34367         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34368             this.fireEvent('loadexception', this);
34369             return;
34370         }
34371         
34372         if(!this.mapTypeId){
34373             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34374         }
34375         
34376         this.gMapContext = this.GMapContext();
34377         
34378         this.initOverlayView();
34379         
34380         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34381         
34382         var _this = this;
34383                 
34384         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34385             _this.setPosition(_this.gMapContext.marker.position);
34386         });
34387         
34388         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34389             _this.fireEvent('mapClick', this, event);
34390             
34391         });
34392
34393         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34394             _this.fireEvent('mapRightClick', this, event);
34395             
34396         });
34397         
34398         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34399             _this.fireEvent('markerClick', this, event);
34400             
34401         });
34402
34403         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34404             _this.fireEvent('markerRightClick', this, event);
34405             
34406         });
34407         
34408         this.setPosition(this.gMapContext.location);
34409         
34410         this.fireEvent('initial', this, this.gMapContext.location);
34411     },
34412     
34413     initOverlayView: function()
34414     {
34415         var _this = this;
34416         
34417         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34418             
34419             draw: function()
34420             {
34421                 _this.fireEvent('OverlayViewDraw', _this);
34422             },
34423             
34424             onAdd: function()
34425             {
34426                 _this.fireEvent('OverlayViewOnAdd', _this);
34427             },
34428             
34429             onRemove: function()
34430             {
34431                 _this.fireEvent('OverlayViewOnRemove', _this);
34432             },
34433             
34434             show: function(cpx)
34435             {
34436                 _this.fireEvent('OverlayViewShow', _this, cpx);
34437             },
34438             
34439             hide: function()
34440             {
34441                 _this.fireEvent('OverlayViewHide', _this);
34442             }
34443             
34444         });
34445     },
34446     
34447     fromLatLngToContainerPixel: function(event)
34448     {
34449         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34450     },
34451     
34452     isApplied: function() 
34453     {
34454         return this.getGmapContext() == false ? false : true;
34455     },
34456     
34457     getGmapContext: function() 
34458     {
34459         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34460     },
34461     
34462     GMapContext: function() 
34463     {
34464         var position = new google.maps.LatLng(this.latitude, this.longitude);
34465         
34466         var _map = new google.maps.Map(this.el.dom, {
34467             center: position,
34468             zoom: this.zoom,
34469             mapTypeId: this.mapTypeId,
34470             mapTypeControl: this.mapTypeControl,
34471             disableDoubleClickZoom: this.disableDoubleClickZoom,
34472             scrollwheel: this.scrollwheel,
34473             streetViewControl: this.streetViewControl,
34474             locationName: this.locationName,
34475             draggable: this.draggable,
34476             enableAutocomplete: this.enableAutocomplete,
34477             enableReverseGeocode: this.enableReverseGeocode
34478         });
34479         
34480         var _marker = new google.maps.Marker({
34481             position: position,
34482             map: _map,
34483             title: this.markerTitle,
34484             draggable: this.draggable
34485         });
34486         
34487         return {
34488             map: _map,
34489             marker: _marker,
34490             circle: null,
34491             location: position,
34492             radius: this.radius,
34493             locationName: this.locationName,
34494             addressComponents: {
34495                 formatted_address: null,
34496                 addressLine1: null,
34497                 addressLine2: null,
34498                 streetName: null,
34499                 streetNumber: null,
34500                 city: null,
34501                 district: null,
34502                 state: null,
34503                 stateOrProvince: null
34504             },
34505             settings: this,
34506             domContainer: this.el.dom,
34507             geodecoder: new google.maps.Geocoder()
34508         };
34509     },
34510     
34511     drawCircle: function(center, radius, options) 
34512     {
34513         if (this.gMapContext.circle != null) {
34514             this.gMapContext.circle.setMap(null);
34515         }
34516         if (radius > 0) {
34517             radius *= 1;
34518             options = Roo.apply({}, options, {
34519                 strokeColor: "#0000FF",
34520                 strokeOpacity: .35,
34521                 strokeWeight: 2,
34522                 fillColor: "#0000FF",
34523                 fillOpacity: .2
34524             });
34525             
34526             options.map = this.gMapContext.map;
34527             options.radius = radius;
34528             options.center = center;
34529             this.gMapContext.circle = new google.maps.Circle(options);
34530             return this.gMapContext.circle;
34531         }
34532         
34533         return null;
34534     },
34535     
34536     setPosition: function(location) 
34537     {
34538         this.gMapContext.location = location;
34539         this.gMapContext.marker.setPosition(location);
34540         this.gMapContext.map.panTo(location);
34541         this.drawCircle(location, this.gMapContext.radius, {});
34542         
34543         var _this = this;
34544         
34545         if (this.gMapContext.settings.enableReverseGeocode) {
34546             this.gMapContext.geodecoder.geocode({
34547                 latLng: this.gMapContext.location
34548             }, function(results, status) {
34549                 
34550                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34551                     _this.gMapContext.locationName = results[0].formatted_address;
34552                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34553                     
34554                     _this.fireEvent('positionchanged', this, location);
34555                 }
34556             });
34557             
34558             return;
34559         }
34560         
34561         this.fireEvent('positionchanged', this, location);
34562     },
34563     
34564     resize: function()
34565     {
34566         google.maps.event.trigger(this.gMapContext.map, "resize");
34567         
34568         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34569         
34570         this.fireEvent('resize', this);
34571     },
34572     
34573     setPositionByLatLng: function(latitude, longitude)
34574     {
34575         this.setPosition(new google.maps.LatLng(latitude, longitude));
34576     },
34577     
34578     getCurrentPosition: function() 
34579     {
34580         return {
34581             latitude: this.gMapContext.location.lat(),
34582             longitude: this.gMapContext.location.lng()
34583         };
34584     },
34585     
34586     getAddressName: function() 
34587     {
34588         return this.gMapContext.locationName;
34589     },
34590     
34591     getAddressComponents: function() 
34592     {
34593         return this.gMapContext.addressComponents;
34594     },
34595     
34596     address_component_from_google_geocode: function(address_components) 
34597     {
34598         var result = {};
34599         
34600         for (var i = 0; i < address_components.length; i++) {
34601             var component = address_components[i];
34602             if (component.types.indexOf("postal_code") >= 0) {
34603                 result.postalCode = component.short_name;
34604             } else if (component.types.indexOf("street_number") >= 0) {
34605                 result.streetNumber = component.short_name;
34606             } else if (component.types.indexOf("route") >= 0) {
34607                 result.streetName = component.short_name;
34608             } else if (component.types.indexOf("neighborhood") >= 0) {
34609                 result.city = component.short_name;
34610             } else if (component.types.indexOf("locality") >= 0) {
34611                 result.city = component.short_name;
34612             } else if (component.types.indexOf("sublocality") >= 0) {
34613                 result.district = component.short_name;
34614             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34615                 result.stateOrProvince = component.short_name;
34616             } else if (component.types.indexOf("country") >= 0) {
34617                 result.country = component.short_name;
34618             }
34619         }
34620         
34621         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34622         result.addressLine2 = "";
34623         return result;
34624     },
34625     
34626     setZoomLevel: function(zoom)
34627     {
34628         this.gMapContext.map.setZoom(zoom);
34629     },
34630     
34631     show: function()
34632     {
34633         if(!this.el){
34634             return;
34635         }
34636         
34637         this.el.show();
34638         
34639         this.resize();
34640         
34641         this.fireEvent('show', this);
34642     },
34643     
34644     hide: function()
34645     {
34646         if(!this.el){
34647             return;
34648         }
34649         
34650         this.el.hide();
34651         
34652         this.fireEvent('hide', this);
34653     }
34654     
34655 });
34656
34657 Roo.apply(Roo.bootstrap.LocationPicker, {
34658     
34659     OverlayView : function(map, options)
34660     {
34661         options = options || {};
34662         
34663         this.setMap(map);
34664     }
34665     
34666     
34667 });/**
34668  * @class Roo.bootstrap.Alert
34669  * @extends Roo.bootstrap.Component
34670  * Bootstrap Alert class - shows an alert area box
34671  * eg
34672  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34673   Enter a valid email address
34674 </div>
34675  * @licence LGPL
34676  * @cfg {String} title The title of alert
34677  * @cfg {String} html The content of alert
34678  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34679  * @cfg {String} fa font-awesomeicon
34680  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34681  * @cfg {Boolean} close true to show a x closer
34682  * 
34683  * 
34684  * @constructor
34685  * Create a new alert
34686  * @param {Object} config The config object
34687  */
34688
34689
34690 Roo.bootstrap.Alert = function(config){
34691     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34692     
34693 };
34694
34695 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34696     
34697     title: '',
34698     html: '',
34699     weight: false,
34700     fa: false,
34701     faicon: false, // BC
34702     close : false,
34703     
34704     
34705     getAutoCreate : function()
34706     {
34707         
34708         var cfg = {
34709             tag : 'div',
34710             cls : 'alert',
34711             cn : [
34712                 {
34713                     tag: 'button',
34714                     type :  "button",
34715                     cls: "close",
34716                     html : '×',
34717                     style : this.close ? '' : 'display:none'
34718                 },
34719                 {
34720                     tag : 'i',
34721                     cls : 'roo-alert-icon'
34722                     
34723                 },
34724                 {
34725                     tag : 'b',
34726                     cls : 'roo-alert-title',
34727                     html : this.title
34728                 },
34729                 {
34730                     tag : 'span',
34731                     cls : 'roo-alert-text',
34732                     html : this.html
34733                 }
34734             ]
34735         };
34736         
34737         if(this.faicon){
34738             cfg.cn[0].cls += ' fa ' + this.faicon;
34739         }
34740         if(this.fa){
34741             cfg.cn[0].cls += ' fa ' + this.fa;
34742         }
34743         
34744         if(this.weight){
34745             cfg.cls += ' alert-' + this.weight;
34746         }
34747         
34748         return cfg;
34749     },
34750     
34751     initEvents: function() 
34752     {
34753         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34754         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34755         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34756         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34757         if (this.seconds > 0) {
34758             this.hide.defer(this.seconds, this);
34759         }
34760     },
34761     /**
34762      * Set the Title Message HTML
34763      * @param {String} html
34764      */
34765     setTitle : function(str)
34766     {
34767         this.titleEl.dom.innerHTML = str;
34768     },
34769      
34770      /**
34771      * Set the Body Message HTML
34772      * @param {String} html
34773      */
34774     setHtml : function(str)
34775     {
34776         this.htmlEl.dom.innerHTML = str;
34777     },
34778     /**
34779      * Set the Weight of the alert
34780      * @param {String} (success|info|warning|danger) weight
34781      */
34782     
34783     setWeight : function(weight)
34784     {
34785         if(this.weight){
34786             this.el.removeClass('alert-' + this.weight);
34787         }
34788         
34789         this.weight = weight;
34790         
34791         this.el.addClass('alert-' + this.weight);
34792     },
34793       /**
34794      * Set the Icon of the alert
34795      * @param {String} see fontawsome names (name without the 'fa-' bit)
34796      */
34797     setIcon : function(icon)
34798     {
34799         if(this.faicon){
34800             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34801         }
34802         
34803         this.faicon = icon;
34804         
34805         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34806     },
34807     /**
34808      * Hide the Alert
34809      */
34810     hide: function() 
34811     {
34812         this.el.hide();   
34813     },
34814     /**
34815      * Show the Alert
34816      */
34817     show: function() 
34818     {  
34819         this.el.show();   
34820     }
34821     
34822 });
34823
34824  
34825 /*
34826 * Licence: LGPL
34827 */
34828
34829 /**
34830  * @class Roo.bootstrap.UploadCropbox
34831  * @extends Roo.bootstrap.Component
34832  * Bootstrap UploadCropbox class
34833  * @cfg {String} emptyText show when image has been loaded
34834  * @cfg {String} rotateNotify show when image too small to rotate
34835  * @cfg {Number} errorTimeout default 3000
34836  * @cfg {Number} minWidth default 300
34837  * @cfg {Number} minHeight default 300
34838  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34839  * @cfg {Boolean} isDocument (true|false) default false
34840  * @cfg {String} url action url
34841  * @cfg {String} paramName default 'imageUpload'
34842  * @cfg {String} method default POST
34843  * @cfg {Boolean} loadMask (true|false) default true
34844  * @cfg {Boolean} loadingText default 'Loading...'
34845  * 
34846  * @constructor
34847  * Create a new UploadCropbox
34848  * @param {Object} config The config object
34849  */
34850
34851 Roo.bootstrap.UploadCropbox = function(config){
34852     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34853     
34854     this.addEvents({
34855         /**
34856          * @event beforeselectfile
34857          * Fire before select file
34858          * @param {Roo.bootstrap.UploadCropbox} this
34859          */
34860         "beforeselectfile" : true,
34861         /**
34862          * @event initial
34863          * Fire after initEvent
34864          * @param {Roo.bootstrap.UploadCropbox} this
34865          */
34866         "initial" : true,
34867         /**
34868          * @event crop
34869          * Fire after initEvent
34870          * @param {Roo.bootstrap.UploadCropbox} this
34871          * @param {String} data
34872          */
34873         "crop" : true,
34874         /**
34875          * @event prepare
34876          * Fire when preparing the file data
34877          * @param {Roo.bootstrap.UploadCropbox} this
34878          * @param {Object} file
34879          */
34880         "prepare" : true,
34881         /**
34882          * @event exception
34883          * Fire when get exception
34884          * @param {Roo.bootstrap.UploadCropbox} this
34885          * @param {XMLHttpRequest} xhr
34886          */
34887         "exception" : true,
34888         /**
34889          * @event beforeloadcanvas
34890          * Fire before load the canvas
34891          * @param {Roo.bootstrap.UploadCropbox} this
34892          * @param {String} src
34893          */
34894         "beforeloadcanvas" : true,
34895         /**
34896          * @event trash
34897          * Fire when trash image
34898          * @param {Roo.bootstrap.UploadCropbox} this
34899          */
34900         "trash" : true,
34901         /**
34902          * @event download
34903          * Fire when download the image
34904          * @param {Roo.bootstrap.UploadCropbox} this
34905          */
34906         "download" : true,
34907         /**
34908          * @event footerbuttonclick
34909          * Fire when footerbuttonclick
34910          * @param {Roo.bootstrap.UploadCropbox} this
34911          * @param {String} type
34912          */
34913         "footerbuttonclick" : true,
34914         /**
34915          * @event resize
34916          * Fire when resize
34917          * @param {Roo.bootstrap.UploadCropbox} this
34918          */
34919         "resize" : true,
34920         /**
34921          * @event rotate
34922          * Fire when rotate the image
34923          * @param {Roo.bootstrap.UploadCropbox} this
34924          * @param {String} pos
34925          */
34926         "rotate" : true,
34927         /**
34928          * @event inspect
34929          * Fire when inspect the file
34930          * @param {Roo.bootstrap.UploadCropbox} this
34931          * @param {Object} file
34932          */
34933         "inspect" : true,
34934         /**
34935          * @event upload
34936          * Fire when xhr upload the file
34937          * @param {Roo.bootstrap.UploadCropbox} this
34938          * @param {Object} data
34939          */
34940         "upload" : true,
34941         /**
34942          * @event arrange
34943          * Fire when arrange the file data
34944          * @param {Roo.bootstrap.UploadCropbox} this
34945          * @param {Object} formData
34946          */
34947         "arrange" : true
34948     });
34949     
34950     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34951 };
34952
34953 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34954     
34955     emptyText : 'Click to upload image',
34956     rotateNotify : 'Image is too small to rotate',
34957     errorTimeout : 3000,
34958     scale : 0,
34959     baseScale : 1,
34960     rotate : 0,
34961     dragable : false,
34962     pinching : false,
34963     mouseX : 0,
34964     mouseY : 0,
34965     cropData : false,
34966     minWidth : 300,
34967     minHeight : 300,
34968     file : false,
34969     exif : {},
34970     baseRotate : 1,
34971     cropType : 'image/jpeg',
34972     buttons : false,
34973     canvasLoaded : false,
34974     isDocument : false,
34975     method : 'POST',
34976     paramName : 'imageUpload',
34977     loadMask : true,
34978     loadingText : 'Loading...',
34979     maskEl : false,
34980     
34981     getAutoCreate : function()
34982     {
34983         var cfg = {
34984             tag : 'div',
34985             cls : 'roo-upload-cropbox',
34986             cn : [
34987                 {
34988                     tag : 'input',
34989                     cls : 'roo-upload-cropbox-selector',
34990                     type : 'file'
34991                 },
34992                 {
34993                     tag : 'div',
34994                     cls : 'roo-upload-cropbox-body',
34995                     style : 'cursor:pointer',
34996                     cn : [
34997                         {
34998                             tag : 'div',
34999                             cls : 'roo-upload-cropbox-preview'
35000                         },
35001                         {
35002                             tag : 'div',
35003                             cls : 'roo-upload-cropbox-thumb'
35004                         },
35005                         {
35006                             tag : 'div',
35007                             cls : 'roo-upload-cropbox-empty-notify',
35008                             html : this.emptyText
35009                         },
35010                         {
35011                             tag : 'div',
35012                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35013                             html : this.rotateNotify
35014                         }
35015                     ]
35016                 },
35017                 {
35018                     tag : 'div',
35019                     cls : 'roo-upload-cropbox-footer',
35020                     cn : {
35021                         tag : 'div',
35022                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35023                         cn : []
35024                     }
35025                 }
35026             ]
35027         };
35028         
35029         return cfg;
35030     },
35031     
35032     onRender : function(ct, position)
35033     {
35034         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35035         
35036         if (this.buttons.length) {
35037             
35038             Roo.each(this.buttons, function(bb) {
35039                 
35040                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35041                 
35042                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35043                 
35044             }, this);
35045         }
35046         
35047         if(this.loadMask){
35048             this.maskEl = this.el;
35049         }
35050     },
35051     
35052     initEvents : function()
35053     {
35054         this.urlAPI = (window.createObjectURL && window) || 
35055                                 (window.URL && URL.revokeObjectURL && URL) || 
35056                                 (window.webkitURL && webkitURL);
35057                         
35058         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35059         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35060         
35061         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35062         this.selectorEl.hide();
35063         
35064         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35065         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35066         
35067         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35068         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35069         this.thumbEl.hide();
35070         
35071         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35072         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35073         
35074         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35075         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35076         this.errorEl.hide();
35077         
35078         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35079         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35080         this.footerEl.hide();
35081         
35082         this.setThumbBoxSize();
35083         
35084         this.bind();
35085         
35086         this.resize();
35087         
35088         this.fireEvent('initial', this);
35089     },
35090
35091     bind : function()
35092     {
35093         var _this = this;
35094         
35095         window.addEventListener("resize", function() { _this.resize(); } );
35096         
35097         this.bodyEl.on('click', this.beforeSelectFile, this);
35098         
35099         if(Roo.isTouch){
35100             this.bodyEl.on('touchstart', this.onTouchStart, this);
35101             this.bodyEl.on('touchmove', this.onTouchMove, this);
35102             this.bodyEl.on('touchend', this.onTouchEnd, this);
35103         }
35104         
35105         if(!Roo.isTouch){
35106             this.bodyEl.on('mousedown', this.onMouseDown, this);
35107             this.bodyEl.on('mousemove', this.onMouseMove, this);
35108             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35109             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35110             Roo.get(document).on('mouseup', this.onMouseUp, this);
35111         }
35112         
35113         this.selectorEl.on('change', this.onFileSelected, this);
35114     },
35115     
35116     reset : function()
35117     {    
35118         this.scale = 0;
35119         this.baseScale = 1;
35120         this.rotate = 0;
35121         this.baseRotate = 1;
35122         this.dragable = false;
35123         this.pinching = false;
35124         this.mouseX = 0;
35125         this.mouseY = 0;
35126         this.cropData = false;
35127         this.notifyEl.dom.innerHTML = this.emptyText;
35128         
35129         this.selectorEl.dom.value = '';
35130         
35131     },
35132     
35133     resize : function()
35134     {
35135         if(this.fireEvent('resize', this) != false){
35136             this.setThumbBoxPosition();
35137             this.setCanvasPosition();
35138         }
35139     },
35140     
35141     onFooterButtonClick : function(e, el, o, type)
35142     {
35143         switch (type) {
35144             case 'rotate-left' :
35145                 this.onRotateLeft(e);
35146                 break;
35147             case 'rotate-right' :
35148                 this.onRotateRight(e);
35149                 break;
35150             case 'picture' :
35151                 this.beforeSelectFile(e);
35152                 break;
35153             case 'trash' :
35154                 this.trash(e);
35155                 break;
35156             case 'crop' :
35157                 this.crop(e);
35158                 break;
35159             case 'download' :
35160                 this.download(e);
35161                 break;
35162             default :
35163                 break;
35164         }
35165         
35166         this.fireEvent('footerbuttonclick', this, type);
35167     },
35168     
35169     beforeSelectFile : function(e)
35170     {
35171         e.preventDefault();
35172         
35173         if(this.fireEvent('beforeselectfile', this) != false){
35174             this.selectorEl.dom.click();
35175         }
35176     },
35177     
35178     onFileSelected : function(e)
35179     {
35180         e.preventDefault();
35181         
35182         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35183             return;
35184         }
35185         
35186         var file = this.selectorEl.dom.files[0];
35187         
35188         if(this.fireEvent('inspect', this, file) != false){
35189             this.prepare(file);
35190         }
35191         
35192     },
35193     
35194     trash : function(e)
35195     {
35196         this.fireEvent('trash', this);
35197     },
35198     
35199     download : function(e)
35200     {
35201         this.fireEvent('download', this);
35202     },
35203     
35204     loadCanvas : function(src)
35205     {   
35206         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35207             
35208             this.reset();
35209             
35210             this.imageEl = document.createElement('img');
35211             
35212             var _this = this;
35213             
35214             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35215             
35216             this.imageEl.src = src;
35217         }
35218     },
35219     
35220     onLoadCanvas : function()
35221     {   
35222         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35223         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35224         
35225         this.bodyEl.un('click', this.beforeSelectFile, this);
35226         
35227         this.notifyEl.hide();
35228         this.thumbEl.show();
35229         this.footerEl.show();
35230         
35231         this.baseRotateLevel();
35232         
35233         if(this.isDocument){
35234             this.setThumbBoxSize();
35235         }
35236         
35237         this.setThumbBoxPosition();
35238         
35239         this.baseScaleLevel();
35240         
35241         this.draw();
35242         
35243         this.resize();
35244         
35245         this.canvasLoaded = true;
35246         
35247         if(this.loadMask){
35248             this.maskEl.unmask();
35249         }
35250         
35251     },
35252     
35253     setCanvasPosition : function()
35254     {   
35255         if(!this.canvasEl){
35256             return;
35257         }
35258         
35259         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35260         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35261         
35262         this.previewEl.setLeft(pw);
35263         this.previewEl.setTop(ph);
35264         
35265     },
35266     
35267     onMouseDown : function(e)
35268     {   
35269         e.stopEvent();
35270         
35271         this.dragable = true;
35272         this.pinching = false;
35273         
35274         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35275             this.dragable = false;
35276             return;
35277         }
35278         
35279         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35280         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35281         
35282     },
35283     
35284     onMouseMove : function(e)
35285     {   
35286         e.stopEvent();
35287         
35288         if(!this.canvasLoaded){
35289             return;
35290         }
35291         
35292         if (!this.dragable){
35293             return;
35294         }
35295         
35296         var minX = Math.ceil(this.thumbEl.getLeft(true));
35297         var minY = Math.ceil(this.thumbEl.getTop(true));
35298         
35299         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35300         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35301         
35302         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35303         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35304         
35305         x = x - this.mouseX;
35306         y = y - this.mouseY;
35307         
35308         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35309         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35310         
35311         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35312         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35313         
35314         this.previewEl.setLeft(bgX);
35315         this.previewEl.setTop(bgY);
35316         
35317         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35318         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35319     },
35320     
35321     onMouseUp : function(e)
35322     {   
35323         e.stopEvent();
35324         
35325         this.dragable = false;
35326     },
35327     
35328     onMouseWheel : function(e)
35329     {   
35330         e.stopEvent();
35331         
35332         this.startScale = this.scale;
35333         
35334         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35335         
35336         if(!this.zoomable()){
35337             this.scale = this.startScale;
35338             return;
35339         }
35340         
35341         this.draw();
35342         
35343         return;
35344     },
35345     
35346     zoomable : function()
35347     {
35348         var minScale = this.thumbEl.getWidth() / this.minWidth;
35349         
35350         if(this.minWidth < this.minHeight){
35351             minScale = this.thumbEl.getHeight() / this.minHeight;
35352         }
35353         
35354         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35355         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35356         
35357         if(
35358                 this.isDocument &&
35359                 (this.rotate == 0 || this.rotate == 180) && 
35360                 (
35361                     width > this.imageEl.OriginWidth || 
35362                     height > this.imageEl.OriginHeight ||
35363                     (width < this.minWidth && height < this.minHeight)
35364                 )
35365         ){
35366             return false;
35367         }
35368         
35369         if(
35370                 this.isDocument &&
35371                 (this.rotate == 90 || this.rotate == 270) && 
35372                 (
35373                     width > this.imageEl.OriginWidth || 
35374                     height > this.imageEl.OriginHeight ||
35375                     (width < this.minHeight && height < this.minWidth)
35376                 )
35377         ){
35378             return false;
35379         }
35380         
35381         if(
35382                 !this.isDocument &&
35383                 (this.rotate == 0 || this.rotate == 180) && 
35384                 (
35385                     width < this.minWidth || 
35386                     width > this.imageEl.OriginWidth || 
35387                     height < this.minHeight || 
35388                     height > this.imageEl.OriginHeight
35389                 )
35390         ){
35391             return false;
35392         }
35393         
35394         if(
35395                 !this.isDocument &&
35396                 (this.rotate == 90 || this.rotate == 270) && 
35397                 (
35398                     width < this.minHeight || 
35399                     width > this.imageEl.OriginWidth || 
35400                     height < this.minWidth || 
35401                     height > this.imageEl.OriginHeight
35402                 )
35403         ){
35404             return false;
35405         }
35406         
35407         return true;
35408         
35409     },
35410     
35411     onRotateLeft : function(e)
35412     {   
35413         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35414             
35415             var minScale = this.thumbEl.getWidth() / this.minWidth;
35416             
35417             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35418             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35419             
35420             this.startScale = this.scale;
35421             
35422             while (this.getScaleLevel() < minScale){
35423             
35424                 this.scale = this.scale + 1;
35425                 
35426                 if(!this.zoomable()){
35427                     break;
35428                 }
35429                 
35430                 if(
35431                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35432                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35433                 ){
35434                     continue;
35435                 }
35436                 
35437                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35438
35439                 this.draw();
35440                 
35441                 return;
35442             }
35443             
35444             this.scale = this.startScale;
35445             
35446             this.onRotateFail();
35447             
35448             return false;
35449         }
35450         
35451         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35452
35453         if(this.isDocument){
35454             this.setThumbBoxSize();
35455             this.setThumbBoxPosition();
35456             this.setCanvasPosition();
35457         }
35458         
35459         this.draw();
35460         
35461         this.fireEvent('rotate', this, 'left');
35462         
35463     },
35464     
35465     onRotateRight : function(e)
35466     {
35467         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35468             
35469             var minScale = this.thumbEl.getWidth() / this.minWidth;
35470         
35471             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35472             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35473             
35474             this.startScale = this.scale;
35475             
35476             while (this.getScaleLevel() < minScale){
35477             
35478                 this.scale = this.scale + 1;
35479                 
35480                 if(!this.zoomable()){
35481                     break;
35482                 }
35483                 
35484                 if(
35485                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35486                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35487                 ){
35488                     continue;
35489                 }
35490                 
35491                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35492
35493                 this.draw();
35494                 
35495                 return;
35496             }
35497             
35498             this.scale = this.startScale;
35499             
35500             this.onRotateFail();
35501             
35502             return false;
35503         }
35504         
35505         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35506
35507         if(this.isDocument){
35508             this.setThumbBoxSize();
35509             this.setThumbBoxPosition();
35510             this.setCanvasPosition();
35511         }
35512         
35513         this.draw();
35514         
35515         this.fireEvent('rotate', this, 'right');
35516     },
35517     
35518     onRotateFail : function()
35519     {
35520         this.errorEl.show(true);
35521         
35522         var _this = this;
35523         
35524         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35525     },
35526     
35527     draw : function()
35528     {
35529         this.previewEl.dom.innerHTML = '';
35530         
35531         var canvasEl = document.createElement("canvas");
35532         
35533         var contextEl = canvasEl.getContext("2d");
35534         
35535         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35536         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35537         var center = this.imageEl.OriginWidth / 2;
35538         
35539         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35540             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35541             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35542             center = this.imageEl.OriginHeight / 2;
35543         }
35544         
35545         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35546         
35547         contextEl.translate(center, center);
35548         contextEl.rotate(this.rotate * Math.PI / 180);
35549
35550         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35551         
35552         this.canvasEl = document.createElement("canvas");
35553         
35554         this.contextEl = this.canvasEl.getContext("2d");
35555         
35556         switch (this.rotate) {
35557             case 0 :
35558                 
35559                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35560                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35561                 
35562                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35563                 
35564                 break;
35565             case 90 : 
35566                 
35567                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35568                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35569                 
35570                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35571                     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);
35572                     break;
35573                 }
35574                 
35575                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35576                 
35577                 break;
35578             case 180 :
35579                 
35580                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35581                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35582                 
35583                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35584                     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);
35585                     break;
35586                 }
35587                 
35588                 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);
35589                 
35590                 break;
35591             case 270 :
35592                 
35593                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35594                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35595         
35596                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35597                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35598                     break;
35599                 }
35600                 
35601                 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);
35602                 
35603                 break;
35604             default : 
35605                 break;
35606         }
35607         
35608         this.previewEl.appendChild(this.canvasEl);
35609         
35610         this.setCanvasPosition();
35611     },
35612     
35613     crop : function()
35614     {
35615         if(!this.canvasLoaded){
35616             return;
35617         }
35618         
35619         var imageCanvas = document.createElement("canvas");
35620         
35621         var imageContext = imageCanvas.getContext("2d");
35622         
35623         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35624         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35625         
35626         var center = imageCanvas.width / 2;
35627         
35628         imageContext.translate(center, center);
35629         
35630         imageContext.rotate(this.rotate * Math.PI / 180);
35631         
35632         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35633         
35634         var canvas = document.createElement("canvas");
35635         
35636         var context = canvas.getContext("2d");
35637                 
35638         canvas.width = this.minWidth;
35639         canvas.height = this.minHeight;
35640
35641         switch (this.rotate) {
35642             case 0 :
35643                 
35644                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35645                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35646                 
35647                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35648                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35649                 
35650                 var targetWidth = this.minWidth - 2 * x;
35651                 var targetHeight = this.minHeight - 2 * y;
35652                 
35653                 var scale = 1;
35654                 
35655                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35656                     scale = targetWidth / width;
35657                 }
35658                 
35659                 if(x > 0 && y == 0){
35660                     scale = targetHeight / height;
35661                 }
35662                 
35663                 if(x > 0 && y > 0){
35664                     scale = targetWidth / width;
35665                     
35666                     if(width < height){
35667                         scale = targetHeight / height;
35668                     }
35669                 }
35670                 
35671                 context.scale(scale, scale);
35672                 
35673                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35674                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35675
35676                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35677                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35678
35679                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35680                 
35681                 break;
35682             case 90 : 
35683                 
35684                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35685                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35686                 
35687                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35688                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35689                 
35690                 var targetWidth = this.minWidth - 2 * x;
35691                 var targetHeight = this.minHeight - 2 * y;
35692                 
35693                 var scale = 1;
35694                 
35695                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35696                     scale = targetWidth / width;
35697                 }
35698                 
35699                 if(x > 0 && y == 0){
35700                     scale = targetHeight / height;
35701                 }
35702                 
35703                 if(x > 0 && y > 0){
35704                     scale = targetWidth / width;
35705                     
35706                     if(width < height){
35707                         scale = targetHeight / height;
35708                     }
35709                 }
35710                 
35711                 context.scale(scale, scale);
35712                 
35713                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35714                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35715
35716                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35717                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35718                 
35719                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35720                 
35721                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35722                 
35723                 break;
35724             case 180 :
35725                 
35726                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35727                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35728                 
35729                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35730                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35731                 
35732                 var targetWidth = this.minWidth - 2 * x;
35733                 var targetHeight = this.minHeight - 2 * y;
35734                 
35735                 var scale = 1;
35736                 
35737                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35738                     scale = targetWidth / width;
35739                 }
35740                 
35741                 if(x > 0 && y == 0){
35742                     scale = targetHeight / height;
35743                 }
35744                 
35745                 if(x > 0 && y > 0){
35746                     scale = targetWidth / width;
35747                     
35748                     if(width < height){
35749                         scale = targetHeight / height;
35750                     }
35751                 }
35752                 
35753                 context.scale(scale, scale);
35754                 
35755                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35756                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35757
35758                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35759                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35760
35761                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35762                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35763                 
35764                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35765                 
35766                 break;
35767             case 270 :
35768                 
35769                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35770                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35771                 
35772                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35773                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35774                 
35775                 var targetWidth = this.minWidth - 2 * x;
35776                 var targetHeight = this.minHeight - 2 * y;
35777                 
35778                 var scale = 1;
35779                 
35780                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35781                     scale = targetWidth / width;
35782                 }
35783                 
35784                 if(x > 0 && y == 0){
35785                     scale = targetHeight / height;
35786                 }
35787                 
35788                 if(x > 0 && y > 0){
35789                     scale = targetWidth / width;
35790                     
35791                     if(width < height){
35792                         scale = targetHeight / height;
35793                     }
35794                 }
35795                 
35796                 context.scale(scale, scale);
35797                 
35798                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35799                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35800
35801                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35802                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35803                 
35804                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35805                 
35806                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35807                 
35808                 break;
35809             default : 
35810                 break;
35811         }
35812         
35813         this.cropData = canvas.toDataURL(this.cropType);
35814         
35815         if(this.fireEvent('crop', this, this.cropData) !== false){
35816             this.process(this.file, this.cropData);
35817         }
35818         
35819         return;
35820         
35821     },
35822     
35823     setThumbBoxSize : function()
35824     {
35825         var width, height;
35826         
35827         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35828             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35829             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35830             
35831             this.minWidth = width;
35832             this.minHeight = height;
35833             
35834             if(this.rotate == 90 || this.rotate == 270){
35835                 this.minWidth = height;
35836                 this.minHeight = width;
35837             }
35838         }
35839         
35840         height = 300;
35841         width = Math.ceil(this.minWidth * height / this.minHeight);
35842         
35843         if(this.minWidth > this.minHeight){
35844             width = 300;
35845             height = Math.ceil(this.minHeight * width / this.minWidth);
35846         }
35847         
35848         this.thumbEl.setStyle({
35849             width : width + 'px',
35850             height : height + 'px'
35851         });
35852
35853         return;
35854             
35855     },
35856     
35857     setThumbBoxPosition : function()
35858     {
35859         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35860         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35861         
35862         this.thumbEl.setLeft(x);
35863         this.thumbEl.setTop(y);
35864         
35865     },
35866     
35867     baseRotateLevel : function()
35868     {
35869         this.baseRotate = 1;
35870         
35871         if(
35872                 typeof(this.exif) != 'undefined' &&
35873                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35874                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35875         ){
35876             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35877         }
35878         
35879         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35880         
35881     },
35882     
35883     baseScaleLevel : function()
35884     {
35885         var width, height;
35886         
35887         if(this.isDocument){
35888             
35889             if(this.baseRotate == 6 || this.baseRotate == 8){
35890             
35891                 height = this.thumbEl.getHeight();
35892                 this.baseScale = height / this.imageEl.OriginWidth;
35893
35894                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35895                     width = this.thumbEl.getWidth();
35896                     this.baseScale = width / this.imageEl.OriginHeight;
35897                 }
35898
35899                 return;
35900             }
35901
35902             height = this.thumbEl.getHeight();
35903             this.baseScale = height / this.imageEl.OriginHeight;
35904
35905             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35906                 width = this.thumbEl.getWidth();
35907                 this.baseScale = width / this.imageEl.OriginWidth;
35908             }
35909
35910             return;
35911         }
35912         
35913         if(this.baseRotate == 6 || this.baseRotate == 8){
35914             
35915             width = this.thumbEl.getHeight();
35916             this.baseScale = width / this.imageEl.OriginHeight;
35917             
35918             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35919                 height = this.thumbEl.getWidth();
35920                 this.baseScale = height / this.imageEl.OriginHeight;
35921             }
35922             
35923             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35924                 height = this.thumbEl.getWidth();
35925                 this.baseScale = height / this.imageEl.OriginHeight;
35926                 
35927                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35928                     width = this.thumbEl.getHeight();
35929                     this.baseScale = width / this.imageEl.OriginWidth;
35930                 }
35931             }
35932             
35933             return;
35934         }
35935         
35936         width = this.thumbEl.getWidth();
35937         this.baseScale = width / this.imageEl.OriginWidth;
35938         
35939         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35940             height = this.thumbEl.getHeight();
35941             this.baseScale = height / this.imageEl.OriginHeight;
35942         }
35943         
35944         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35945             
35946             height = this.thumbEl.getHeight();
35947             this.baseScale = height / this.imageEl.OriginHeight;
35948             
35949             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35950                 width = this.thumbEl.getWidth();
35951                 this.baseScale = width / this.imageEl.OriginWidth;
35952             }
35953             
35954         }
35955         
35956         return;
35957     },
35958     
35959     getScaleLevel : function()
35960     {
35961         return this.baseScale * Math.pow(1.1, this.scale);
35962     },
35963     
35964     onTouchStart : function(e)
35965     {
35966         if(!this.canvasLoaded){
35967             this.beforeSelectFile(e);
35968             return;
35969         }
35970         
35971         var touches = e.browserEvent.touches;
35972         
35973         if(!touches){
35974             return;
35975         }
35976         
35977         if(touches.length == 1){
35978             this.onMouseDown(e);
35979             return;
35980         }
35981         
35982         if(touches.length != 2){
35983             return;
35984         }
35985         
35986         var coords = [];
35987         
35988         for(var i = 0, finger; finger = touches[i]; i++){
35989             coords.push(finger.pageX, finger.pageY);
35990         }
35991         
35992         var x = Math.pow(coords[0] - coords[2], 2);
35993         var y = Math.pow(coords[1] - coords[3], 2);
35994         
35995         this.startDistance = Math.sqrt(x + y);
35996         
35997         this.startScale = this.scale;
35998         
35999         this.pinching = true;
36000         this.dragable = false;
36001         
36002     },
36003     
36004     onTouchMove : function(e)
36005     {
36006         if(!this.pinching && !this.dragable){
36007             return;
36008         }
36009         
36010         var touches = e.browserEvent.touches;
36011         
36012         if(!touches){
36013             return;
36014         }
36015         
36016         if(this.dragable){
36017             this.onMouseMove(e);
36018             return;
36019         }
36020         
36021         var coords = [];
36022         
36023         for(var i = 0, finger; finger = touches[i]; i++){
36024             coords.push(finger.pageX, finger.pageY);
36025         }
36026         
36027         var x = Math.pow(coords[0] - coords[2], 2);
36028         var y = Math.pow(coords[1] - coords[3], 2);
36029         
36030         this.endDistance = Math.sqrt(x + y);
36031         
36032         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36033         
36034         if(!this.zoomable()){
36035             this.scale = this.startScale;
36036             return;
36037         }
36038         
36039         this.draw();
36040         
36041     },
36042     
36043     onTouchEnd : function(e)
36044     {
36045         this.pinching = false;
36046         this.dragable = false;
36047         
36048     },
36049     
36050     process : function(file, crop)
36051     {
36052         if(this.loadMask){
36053             this.maskEl.mask(this.loadingText);
36054         }
36055         
36056         this.xhr = new XMLHttpRequest();
36057         
36058         file.xhr = this.xhr;
36059
36060         this.xhr.open(this.method, this.url, true);
36061         
36062         var headers = {
36063             "Accept": "application/json",
36064             "Cache-Control": "no-cache",
36065             "X-Requested-With": "XMLHttpRequest"
36066         };
36067         
36068         for (var headerName in headers) {
36069             var headerValue = headers[headerName];
36070             if (headerValue) {
36071                 this.xhr.setRequestHeader(headerName, headerValue);
36072             }
36073         }
36074         
36075         var _this = this;
36076         
36077         this.xhr.onload = function()
36078         {
36079             _this.xhrOnLoad(_this.xhr);
36080         }
36081         
36082         this.xhr.onerror = function()
36083         {
36084             _this.xhrOnError(_this.xhr);
36085         }
36086         
36087         var formData = new FormData();
36088
36089         formData.append('returnHTML', 'NO');
36090         
36091         if(crop){
36092             formData.append('crop', crop);
36093         }
36094         
36095         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36096             formData.append(this.paramName, file, file.name);
36097         }
36098         
36099         if(typeof(file.filename) != 'undefined'){
36100             formData.append('filename', file.filename);
36101         }
36102         
36103         if(typeof(file.mimetype) != 'undefined'){
36104             formData.append('mimetype', file.mimetype);
36105         }
36106         
36107         if(this.fireEvent('arrange', this, formData) != false){
36108             this.xhr.send(formData);
36109         };
36110     },
36111     
36112     xhrOnLoad : function(xhr)
36113     {
36114         if(this.loadMask){
36115             this.maskEl.unmask();
36116         }
36117         
36118         if (xhr.readyState !== 4) {
36119             this.fireEvent('exception', this, xhr);
36120             return;
36121         }
36122
36123         var response = Roo.decode(xhr.responseText);
36124         
36125         if(!response.success){
36126             this.fireEvent('exception', this, xhr);
36127             return;
36128         }
36129         
36130         var response = Roo.decode(xhr.responseText);
36131         
36132         this.fireEvent('upload', this, response);
36133         
36134     },
36135     
36136     xhrOnError : function()
36137     {
36138         if(this.loadMask){
36139             this.maskEl.unmask();
36140         }
36141         
36142         Roo.log('xhr on error');
36143         
36144         var response = Roo.decode(xhr.responseText);
36145           
36146         Roo.log(response);
36147         
36148     },
36149     
36150     prepare : function(file)
36151     {   
36152         if(this.loadMask){
36153             this.maskEl.mask(this.loadingText);
36154         }
36155         
36156         this.file = false;
36157         this.exif = {};
36158         
36159         if(typeof(file) === 'string'){
36160             this.loadCanvas(file);
36161             return;
36162         }
36163         
36164         if(!file || !this.urlAPI){
36165             return;
36166         }
36167         
36168         this.file = file;
36169         this.cropType = file.type;
36170         
36171         var _this = this;
36172         
36173         if(this.fireEvent('prepare', this, this.file) != false){
36174             
36175             var reader = new FileReader();
36176             
36177             reader.onload = function (e) {
36178                 if (e.target.error) {
36179                     Roo.log(e.target.error);
36180                     return;
36181                 }
36182                 
36183                 var buffer = e.target.result,
36184                     dataView = new DataView(buffer),
36185                     offset = 2,
36186                     maxOffset = dataView.byteLength - 4,
36187                     markerBytes,
36188                     markerLength;
36189                 
36190                 if (dataView.getUint16(0) === 0xffd8) {
36191                     while (offset < maxOffset) {
36192                         markerBytes = dataView.getUint16(offset);
36193                         
36194                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36195                             markerLength = dataView.getUint16(offset + 2) + 2;
36196                             if (offset + markerLength > dataView.byteLength) {
36197                                 Roo.log('Invalid meta data: Invalid segment size.');
36198                                 break;
36199                             }
36200                             
36201                             if(markerBytes == 0xffe1){
36202                                 _this.parseExifData(
36203                                     dataView,
36204                                     offset,
36205                                     markerLength
36206                                 );
36207                             }
36208                             
36209                             offset += markerLength;
36210                             
36211                             continue;
36212                         }
36213                         
36214                         break;
36215                     }
36216                     
36217                 }
36218                 
36219                 var url = _this.urlAPI.createObjectURL(_this.file);
36220                 
36221                 _this.loadCanvas(url);
36222                 
36223                 return;
36224             }
36225             
36226             reader.readAsArrayBuffer(this.file);
36227             
36228         }
36229         
36230     },
36231     
36232     parseExifData : function(dataView, offset, length)
36233     {
36234         var tiffOffset = offset + 10,
36235             littleEndian,
36236             dirOffset;
36237     
36238         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36239             // No Exif data, might be XMP data instead
36240             return;
36241         }
36242         
36243         // Check for the ASCII code for "Exif" (0x45786966):
36244         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36245             // No Exif data, might be XMP data instead
36246             return;
36247         }
36248         if (tiffOffset + 8 > dataView.byteLength) {
36249             Roo.log('Invalid Exif data: Invalid segment size.');
36250             return;
36251         }
36252         // Check for the two null bytes:
36253         if (dataView.getUint16(offset + 8) !== 0x0000) {
36254             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36255             return;
36256         }
36257         // Check the byte alignment:
36258         switch (dataView.getUint16(tiffOffset)) {
36259         case 0x4949:
36260             littleEndian = true;
36261             break;
36262         case 0x4D4D:
36263             littleEndian = false;
36264             break;
36265         default:
36266             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36267             return;
36268         }
36269         // Check for the TIFF tag marker (0x002A):
36270         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36271             Roo.log('Invalid Exif data: Missing TIFF marker.');
36272             return;
36273         }
36274         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36275         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36276         
36277         this.parseExifTags(
36278             dataView,
36279             tiffOffset,
36280             tiffOffset + dirOffset,
36281             littleEndian
36282         );
36283     },
36284     
36285     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36286     {
36287         var tagsNumber,
36288             dirEndOffset,
36289             i;
36290         if (dirOffset + 6 > dataView.byteLength) {
36291             Roo.log('Invalid Exif data: Invalid directory offset.');
36292             return;
36293         }
36294         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36295         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36296         if (dirEndOffset + 4 > dataView.byteLength) {
36297             Roo.log('Invalid Exif data: Invalid directory size.');
36298             return;
36299         }
36300         for (i = 0; i < tagsNumber; i += 1) {
36301             this.parseExifTag(
36302                 dataView,
36303                 tiffOffset,
36304                 dirOffset + 2 + 12 * i, // tag offset
36305                 littleEndian
36306             );
36307         }
36308         // Return the offset to the next directory:
36309         return dataView.getUint32(dirEndOffset, littleEndian);
36310     },
36311     
36312     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36313     {
36314         var tag = dataView.getUint16(offset, littleEndian);
36315         
36316         this.exif[tag] = this.getExifValue(
36317             dataView,
36318             tiffOffset,
36319             offset,
36320             dataView.getUint16(offset + 2, littleEndian), // tag type
36321             dataView.getUint32(offset + 4, littleEndian), // tag length
36322             littleEndian
36323         );
36324     },
36325     
36326     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36327     {
36328         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36329             tagSize,
36330             dataOffset,
36331             values,
36332             i,
36333             str,
36334             c;
36335     
36336         if (!tagType) {
36337             Roo.log('Invalid Exif data: Invalid tag type.');
36338             return;
36339         }
36340         
36341         tagSize = tagType.size * length;
36342         // Determine if the value is contained in the dataOffset bytes,
36343         // or if the value at the dataOffset is a pointer to the actual data:
36344         dataOffset = tagSize > 4 ?
36345                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36346         if (dataOffset + tagSize > dataView.byteLength) {
36347             Roo.log('Invalid Exif data: Invalid data offset.');
36348             return;
36349         }
36350         if (length === 1) {
36351             return tagType.getValue(dataView, dataOffset, littleEndian);
36352         }
36353         values = [];
36354         for (i = 0; i < length; i += 1) {
36355             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36356         }
36357         
36358         if (tagType.ascii) {
36359             str = '';
36360             // Concatenate the chars:
36361             for (i = 0; i < values.length; i += 1) {
36362                 c = values[i];
36363                 // Ignore the terminating NULL byte(s):
36364                 if (c === '\u0000') {
36365                     break;
36366                 }
36367                 str += c;
36368             }
36369             return str;
36370         }
36371         return values;
36372     }
36373     
36374 });
36375
36376 Roo.apply(Roo.bootstrap.UploadCropbox, {
36377     tags : {
36378         'Orientation': 0x0112
36379     },
36380     
36381     Orientation: {
36382             1: 0, //'top-left',
36383 //            2: 'top-right',
36384             3: 180, //'bottom-right',
36385 //            4: 'bottom-left',
36386 //            5: 'left-top',
36387             6: 90, //'right-top',
36388 //            7: 'right-bottom',
36389             8: 270 //'left-bottom'
36390     },
36391     
36392     exifTagTypes : {
36393         // byte, 8-bit unsigned int:
36394         1: {
36395             getValue: function (dataView, dataOffset) {
36396                 return dataView.getUint8(dataOffset);
36397             },
36398             size: 1
36399         },
36400         // ascii, 8-bit byte:
36401         2: {
36402             getValue: function (dataView, dataOffset) {
36403                 return String.fromCharCode(dataView.getUint8(dataOffset));
36404             },
36405             size: 1,
36406             ascii: true
36407         },
36408         // short, 16 bit int:
36409         3: {
36410             getValue: function (dataView, dataOffset, littleEndian) {
36411                 return dataView.getUint16(dataOffset, littleEndian);
36412             },
36413             size: 2
36414         },
36415         // long, 32 bit int:
36416         4: {
36417             getValue: function (dataView, dataOffset, littleEndian) {
36418                 return dataView.getUint32(dataOffset, littleEndian);
36419             },
36420             size: 4
36421         },
36422         // rational = two long values, first is numerator, second is denominator:
36423         5: {
36424             getValue: function (dataView, dataOffset, littleEndian) {
36425                 return dataView.getUint32(dataOffset, littleEndian) /
36426                     dataView.getUint32(dataOffset + 4, littleEndian);
36427             },
36428             size: 8
36429         },
36430         // slong, 32 bit signed int:
36431         9: {
36432             getValue: function (dataView, dataOffset, littleEndian) {
36433                 return dataView.getInt32(dataOffset, littleEndian);
36434             },
36435             size: 4
36436         },
36437         // srational, two slongs, first is numerator, second is denominator:
36438         10: {
36439             getValue: function (dataView, dataOffset, littleEndian) {
36440                 return dataView.getInt32(dataOffset, littleEndian) /
36441                     dataView.getInt32(dataOffset + 4, littleEndian);
36442             },
36443             size: 8
36444         }
36445     },
36446     
36447     footer : {
36448         STANDARD : [
36449             {
36450                 tag : 'div',
36451                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36452                 action : 'rotate-left',
36453                 cn : [
36454                     {
36455                         tag : 'button',
36456                         cls : 'btn btn-default',
36457                         html : '<i class="fa fa-undo"></i>'
36458                     }
36459                 ]
36460             },
36461             {
36462                 tag : 'div',
36463                 cls : 'btn-group roo-upload-cropbox-picture',
36464                 action : 'picture',
36465                 cn : [
36466                     {
36467                         tag : 'button',
36468                         cls : 'btn btn-default',
36469                         html : '<i class="fa fa-picture-o"></i>'
36470                     }
36471                 ]
36472             },
36473             {
36474                 tag : 'div',
36475                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36476                 action : 'rotate-right',
36477                 cn : [
36478                     {
36479                         tag : 'button',
36480                         cls : 'btn btn-default',
36481                         html : '<i class="fa fa-repeat"></i>'
36482                     }
36483                 ]
36484             }
36485         ],
36486         DOCUMENT : [
36487             {
36488                 tag : 'div',
36489                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36490                 action : 'rotate-left',
36491                 cn : [
36492                     {
36493                         tag : 'button',
36494                         cls : 'btn btn-default',
36495                         html : '<i class="fa fa-undo"></i>'
36496                     }
36497                 ]
36498             },
36499             {
36500                 tag : 'div',
36501                 cls : 'btn-group roo-upload-cropbox-download',
36502                 action : 'download',
36503                 cn : [
36504                     {
36505                         tag : 'button',
36506                         cls : 'btn btn-default',
36507                         html : '<i class="fa fa-download"></i>'
36508                     }
36509                 ]
36510             },
36511             {
36512                 tag : 'div',
36513                 cls : 'btn-group roo-upload-cropbox-crop',
36514                 action : 'crop',
36515                 cn : [
36516                     {
36517                         tag : 'button',
36518                         cls : 'btn btn-default',
36519                         html : '<i class="fa fa-crop"></i>'
36520                     }
36521                 ]
36522             },
36523             {
36524                 tag : 'div',
36525                 cls : 'btn-group roo-upload-cropbox-trash',
36526                 action : 'trash',
36527                 cn : [
36528                     {
36529                         tag : 'button',
36530                         cls : 'btn btn-default',
36531                         html : '<i class="fa fa-trash"></i>'
36532                     }
36533                 ]
36534             },
36535             {
36536                 tag : 'div',
36537                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36538                 action : 'rotate-right',
36539                 cn : [
36540                     {
36541                         tag : 'button',
36542                         cls : 'btn btn-default',
36543                         html : '<i class="fa fa-repeat"></i>'
36544                     }
36545                 ]
36546             }
36547         ],
36548         ROTATOR : [
36549             {
36550                 tag : 'div',
36551                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36552                 action : 'rotate-left',
36553                 cn : [
36554                     {
36555                         tag : 'button',
36556                         cls : 'btn btn-default',
36557                         html : '<i class="fa fa-undo"></i>'
36558                     }
36559                 ]
36560             },
36561             {
36562                 tag : 'div',
36563                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36564                 action : 'rotate-right',
36565                 cn : [
36566                     {
36567                         tag : 'button',
36568                         cls : 'btn btn-default',
36569                         html : '<i class="fa fa-repeat"></i>'
36570                     }
36571                 ]
36572             }
36573         ]
36574     }
36575 });
36576
36577 /*
36578 * Licence: LGPL
36579 */
36580
36581 /**
36582  * @class Roo.bootstrap.DocumentManager
36583  * @extends Roo.bootstrap.Component
36584  * Bootstrap DocumentManager class
36585  * @cfg {String} paramName default 'imageUpload'
36586  * @cfg {String} toolTipName default 'filename'
36587  * @cfg {String} method default POST
36588  * @cfg {String} url action url
36589  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36590  * @cfg {Boolean} multiple multiple upload default true
36591  * @cfg {Number} thumbSize default 300
36592  * @cfg {String} fieldLabel
36593  * @cfg {Number} labelWidth default 4
36594  * @cfg {String} labelAlign (left|top) default left
36595  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36596 * @cfg {Number} labellg set the width of label (1-12)
36597  * @cfg {Number} labelmd set the width of label (1-12)
36598  * @cfg {Number} labelsm set the width of label (1-12)
36599  * @cfg {Number} labelxs set the width of label (1-12)
36600  * 
36601  * @constructor
36602  * Create a new DocumentManager
36603  * @param {Object} config The config object
36604  */
36605
36606 Roo.bootstrap.DocumentManager = function(config){
36607     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36608     
36609     this.files = [];
36610     this.delegates = [];
36611     
36612     this.addEvents({
36613         /**
36614          * @event initial
36615          * Fire when initial the DocumentManager
36616          * @param {Roo.bootstrap.DocumentManager} this
36617          */
36618         "initial" : true,
36619         /**
36620          * @event inspect
36621          * inspect selected file
36622          * @param {Roo.bootstrap.DocumentManager} this
36623          * @param {File} file
36624          */
36625         "inspect" : true,
36626         /**
36627          * @event exception
36628          * Fire when xhr load exception
36629          * @param {Roo.bootstrap.DocumentManager} this
36630          * @param {XMLHttpRequest} xhr
36631          */
36632         "exception" : true,
36633         /**
36634          * @event afterupload
36635          * Fire when xhr load exception
36636          * @param {Roo.bootstrap.DocumentManager} this
36637          * @param {XMLHttpRequest} xhr
36638          */
36639         "afterupload" : true,
36640         /**
36641          * @event prepare
36642          * prepare the form data
36643          * @param {Roo.bootstrap.DocumentManager} this
36644          * @param {Object} formData
36645          */
36646         "prepare" : true,
36647         /**
36648          * @event remove
36649          * Fire when remove the file
36650          * @param {Roo.bootstrap.DocumentManager} this
36651          * @param {Object} file
36652          */
36653         "remove" : true,
36654         /**
36655          * @event refresh
36656          * Fire after refresh the file
36657          * @param {Roo.bootstrap.DocumentManager} this
36658          */
36659         "refresh" : true,
36660         /**
36661          * @event click
36662          * Fire after click the image
36663          * @param {Roo.bootstrap.DocumentManager} this
36664          * @param {Object} file
36665          */
36666         "click" : true,
36667         /**
36668          * @event edit
36669          * Fire when upload a image and editable set to true
36670          * @param {Roo.bootstrap.DocumentManager} this
36671          * @param {Object} file
36672          */
36673         "edit" : true,
36674         /**
36675          * @event beforeselectfile
36676          * Fire before select file
36677          * @param {Roo.bootstrap.DocumentManager} this
36678          */
36679         "beforeselectfile" : true,
36680         /**
36681          * @event process
36682          * Fire before process file
36683          * @param {Roo.bootstrap.DocumentManager} this
36684          * @param {Object} file
36685          */
36686         "process" : true,
36687         /**
36688          * @event previewrendered
36689          * Fire when preview rendered
36690          * @param {Roo.bootstrap.DocumentManager} this
36691          * @param {Object} file
36692          */
36693         "previewrendered" : true,
36694         /**
36695          */
36696         "previewResize" : true
36697         
36698     });
36699 };
36700
36701 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36702     
36703     boxes : 0,
36704     inputName : '',
36705     thumbSize : 300,
36706     multiple : true,
36707     files : false,
36708     method : 'POST',
36709     url : '',
36710     paramName : 'imageUpload',
36711     toolTipName : 'filename',
36712     fieldLabel : '',
36713     labelWidth : 4,
36714     labelAlign : 'left',
36715     editable : true,
36716     delegates : false,
36717     xhr : false, 
36718     
36719     labellg : 0,
36720     labelmd : 0,
36721     labelsm : 0,
36722     labelxs : 0,
36723     
36724     getAutoCreate : function()
36725     {   
36726         var managerWidget = {
36727             tag : 'div',
36728             cls : 'roo-document-manager',
36729             cn : [
36730                 {
36731                     tag : 'input',
36732                     cls : 'roo-document-manager-selector',
36733                     type : 'file'
36734                 },
36735                 {
36736                     tag : 'div',
36737                     cls : 'roo-document-manager-uploader',
36738                     cn : [
36739                         {
36740                             tag : 'div',
36741                             cls : 'roo-document-manager-upload-btn',
36742                             html : '<i class="fa fa-plus"></i>'
36743                         }
36744                     ]
36745                     
36746                 }
36747             ]
36748         };
36749         
36750         var content = [
36751             {
36752                 tag : 'div',
36753                 cls : 'column col-md-12',
36754                 cn : managerWidget
36755             }
36756         ];
36757         
36758         if(this.fieldLabel.length){
36759             
36760             content = [
36761                 {
36762                     tag : 'div',
36763                     cls : 'column col-md-12',
36764                     html : this.fieldLabel
36765                 },
36766                 {
36767                     tag : 'div',
36768                     cls : 'column col-md-12',
36769                     cn : managerWidget
36770                 }
36771             ];
36772
36773             if(this.labelAlign == 'left'){
36774                 content = [
36775                     {
36776                         tag : 'div',
36777                         cls : 'column',
36778                         html : this.fieldLabel
36779                     },
36780                     {
36781                         tag : 'div',
36782                         cls : 'column',
36783                         cn : managerWidget
36784                     }
36785                 ];
36786                 
36787                 if(this.labelWidth > 12){
36788                     content[0].style = "width: " + this.labelWidth + 'px';
36789                 }
36790
36791                 if(this.labelWidth < 13 && this.labelmd == 0){
36792                     this.labelmd = this.labelWidth;
36793                 }
36794
36795                 if(this.labellg > 0){
36796                     content[0].cls += ' col-lg-' + this.labellg;
36797                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36798                 }
36799
36800                 if(this.labelmd > 0){
36801                     content[0].cls += ' col-md-' + this.labelmd;
36802                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36803                 }
36804
36805                 if(this.labelsm > 0){
36806                     content[0].cls += ' col-sm-' + this.labelsm;
36807                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36808                 }
36809
36810                 if(this.labelxs > 0){
36811                     content[0].cls += ' col-xs-' + this.labelxs;
36812                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36813                 }
36814                 
36815             }
36816         }
36817         
36818         var cfg = {
36819             tag : 'div',
36820             cls : 'row clearfix',
36821             cn : content
36822         };
36823         
36824         return cfg;
36825         
36826     },
36827     
36828     initEvents : function()
36829     {
36830         this.managerEl = this.el.select('.roo-document-manager', true).first();
36831         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36832         
36833         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36834         this.selectorEl.hide();
36835         
36836         if(this.multiple){
36837             this.selectorEl.attr('multiple', 'multiple');
36838         }
36839         
36840         this.selectorEl.on('change', this.onFileSelected, this);
36841         
36842         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36843         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36844         
36845         this.uploader.on('click', this.onUploaderClick, this);
36846         
36847         this.renderProgressDialog();
36848         
36849         var _this = this;
36850         
36851         window.addEventListener("resize", function() { _this.refresh(); } );
36852         
36853         this.fireEvent('initial', this);
36854     },
36855     
36856     renderProgressDialog : function()
36857     {
36858         var _this = this;
36859         
36860         this.progressDialog = new Roo.bootstrap.Modal({
36861             cls : 'roo-document-manager-progress-dialog',
36862             allow_close : false,
36863             animate : false,
36864             title : '',
36865             buttons : [
36866                 {
36867                     name  :'cancel',
36868                     weight : 'danger',
36869                     html : 'Cancel'
36870                 }
36871             ], 
36872             listeners : { 
36873                 btnclick : function() {
36874                     _this.uploadCancel();
36875                     this.hide();
36876                 }
36877             }
36878         });
36879          
36880         this.progressDialog.render(Roo.get(document.body));
36881          
36882         this.progress = new Roo.bootstrap.Progress({
36883             cls : 'roo-document-manager-progress',
36884             active : true,
36885             striped : true
36886         });
36887         
36888         this.progress.render(this.progressDialog.getChildContainer());
36889         
36890         this.progressBar = new Roo.bootstrap.ProgressBar({
36891             cls : 'roo-document-manager-progress-bar',
36892             aria_valuenow : 0,
36893             aria_valuemin : 0,
36894             aria_valuemax : 12,
36895             panel : 'success'
36896         });
36897         
36898         this.progressBar.render(this.progress.getChildContainer());
36899     },
36900     
36901     onUploaderClick : function(e)
36902     {
36903         e.preventDefault();
36904      
36905         if(this.fireEvent('beforeselectfile', this) != false){
36906             this.selectorEl.dom.click();
36907         }
36908         
36909     },
36910     
36911     onFileSelected : function(e)
36912     {
36913         e.preventDefault();
36914         
36915         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36916             return;
36917         }
36918         
36919         Roo.each(this.selectorEl.dom.files, function(file){
36920             if(this.fireEvent('inspect', this, file) != false){
36921                 this.files.push(file);
36922             }
36923         }, this);
36924         
36925         this.queue();
36926         
36927     },
36928     
36929     queue : function()
36930     {
36931         this.selectorEl.dom.value = '';
36932         
36933         if(!this.files || !this.files.length){
36934             return;
36935         }
36936         
36937         if(this.boxes > 0 && this.files.length > this.boxes){
36938             this.files = this.files.slice(0, this.boxes);
36939         }
36940         
36941         this.uploader.show();
36942         
36943         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36944             this.uploader.hide();
36945         }
36946         
36947         var _this = this;
36948         
36949         var files = [];
36950         
36951         var docs = [];
36952         
36953         Roo.each(this.files, function(file){
36954             
36955             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36956                 var f = this.renderPreview(file);
36957                 files.push(f);
36958                 return;
36959             }
36960             
36961             if(file.type.indexOf('image') != -1){
36962                 this.delegates.push(
36963                     (function(){
36964                         _this.process(file);
36965                     }).createDelegate(this)
36966                 );
36967         
36968                 return;
36969             }
36970             
36971             docs.push(
36972                 (function(){
36973                     _this.process(file);
36974                 }).createDelegate(this)
36975             );
36976             
36977         }, this);
36978         
36979         this.files = files;
36980         
36981         this.delegates = this.delegates.concat(docs);
36982         
36983         if(!this.delegates.length){
36984             this.refresh();
36985             return;
36986         }
36987         
36988         this.progressBar.aria_valuemax = this.delegates.length;
36989         
36990         this.arrange();
36991         
36992         return;
36993     },
36994     
36995     arrange : function()
36996     {
36997         if(!this.delegates.length){
36998             this.progressDialog.hide();
36999             this.refresh();
37000             return;
37001         }
37002         
37003         var delegate = this.delegates.shift();
37004         
37005         this.progressDialog.show();
37006         
37007         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37008         
37009         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37010         
37011         delegate();
37012     },
37013     
37014     refresh : function()
37015     {
37016         this.uploader.show();
37017         
37018         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37019             this.uploader.hide();
37020         }
37021         
37022         Roo.isTouch ? this.closable(false) : this.closable(true);
37023         
37024         this.fireEvent('refresh', this);
37025     },
37026     
37027     onRemove : function(e, el, o)
37028     {
37029         e.preventDefault();
37030         
37031         this.fireEvent('remove', this, o);
37032         
37033     },
37034     
37035     remove : function(o)
37036     {
37037         var files = [];
37038         
37039         Roo.each(this.files, function(file){
37040             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37041                 files.push(file);
37042                 return;
37043             }
37044
37045             o.target.remove();
37046
37047         }, this);
37048         
37049         this.files = files;
37050         
37051         this.refresh();
37052     },
37053     
37054     clear : function()
37055     {
37056         Roo.each(this.files, function(file){
37057             if(!file.target){
37058                 return;
37059             }
37060             
37061             file.target.remove();
37062
37063         }, this);
37064         
37065         this.files = [];
37066         
37067         this.refresh();
37068     },
37069     
37070     onClick : function(e, el, o)
37071     {
37072         e.preventDefault();
37073         
37074         this.fireEvent('click', this, o);
37075         
37076     },
37077     
37078     closable : function(closable)
37079     {
37080         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37081             
37082             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37083             
37084             if(closable){
37085                 el.show();
37086                 return;
37087             }
37088             
37089             el.hide();
37090             
37091         }, this);
37092     },
37093     
37094     xhrOnLoad : function(xhr)
37095     {
37096         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37097             el.remove();
37098         }, this);
37099         
37100         if (xhr.readyState !== 4) {
37101             this.arrange();
37102             this.fireEvent('exception', this, xhr);
37103             return;
37104         }
37105
37106         var response = Roo.decode(xhr.responseText);
37107         
37108         if(!response.success){
37109             this.arrange();
37110             this.fireEvent('exception', this, xhr);
37111             return;
37112         }
37113         
37114         var file = this.renderPreview(response.data);
37115         
37116         this.files.push(file);
37117         
37118         this.arrange();
37119         
37120         this.fireEvent('afterupload', this, xhr);
37121         
37122     },
37123     
37124     xhrOnError : function(xhr)
37125     {
37126         Roo.log('xhr on error');
37127         
37128         var response = Roo.decode(xhr.responseText);
37129           
37130         Roo.log(response);
37131         
37132         this.arrange();
37133     },
37134     
37135     process : function(file)
37136     {
37137         if(this.fireEvent('process', this, file) !== false){
37138             if(this.editable && file.type.indexOf('image') != -1){
37139                 this.fireEvent('edit', this, file);
37140                 return;
37141             }
37142
37143             this.uploadStart(file, false);
37144
37145             return;
37146         }
37147         
37148     },
37149     
37150     uploadStart : function(file, crop)
37151     {
37152         this.xhr = new XMLHttpRequest();
37153         
37154         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37155             this.arrange();
37156             return;
37157         }
37158         
37159         file.xhr = this.xhr;
37160             
37161         this.managerEl.createChild({
37162             tag : 'div',
37163             cls : 'roo-document-manager-loading',
37164             cn : [
37165                 {
37166                     tag : 'div',
37167                     tooltip : file.name,
37168                     cls : 'roo-document-manager-thumb',
37169                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37170                 }
37171             ]
37172
37173         });
37174
37175         this.xhr.open(this.method, this.url, true);
37176         
37177         var headers = {
37178             "Accept": "application/json",
37179             "Cache-Control": "no-cache",
37180             "X-Requested-With": "XMLHttpRequest"
37181         };
37182         
37183         for (var headerName in headers) {
37184             var headerValue = headers[headerName];
37185             if (headerValue) {
37186                 this.xhr.setRequestHeader(headerName, headerValue);
37187             }
37188         }
37189         
37190         var _this = this;
37191         
37192         this.xhr.onload = function()
37193         {
37194             _this.xhrOnLoad(_this.xhr);
37195         }
37196         
37197         this.xhr.onerror = function()
37198         {
37199             _this.xhrOnError(_this.xhr);
37200         }
37201         
37202         var formData = new FormData();
37203
37204         formData.append('returnHTML', 'NO');
37205         
37206         if(crop){
37207             formData.append('crop', crop);
37208         }
37209         
37210         formData.append(this.paramName, file, file.name);
37211         
37212         var options = {
37213             file : file, 
37214             manually : false
37215         };
37216         
37217         if(this.fireEvent('prepare', this, formData, options) != false){
37218             
37219             if(options.manually){
37220                 return;
37221             }
37222             
37223             this.xhr.send(formData);
37224             return;
37225         };
37226         
37227         this.uploadCancel();
37228     },
37229     
37230     uploadCancel : function()
37231     {
37232         if (this.xhr) {
37233             this.xhr.abort();
37234         }
37235         
37236         this.delegates = [];
37237         
37238         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37239             el.remove();
37240         }, this);
37241         
37242         this.arrange();
37243     },
37244     
37245     renderPreview : function(file)
37246     {
37247         if(typeof(file.target) != 'undefined' && file.target){
37248             return file;
37249         }
37250         
37251         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37252         
37253         var previewEl = this.managerEl.createChild({
37254             tag : 'div',
37255             cls : 'roo-document-manager-preview',
37256             cn : [
37257                 {
37258                     tag : 'div',
37259                     tooltip : file[this.toolTipName],
37260                     cls : 'roo-document-manager-thumb',
37261                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37262                 },
37263                 {
37264                     tag : 'button',
37265                     cls : 'close',
37266                     html : '<i class="fa fa-times-circle"></i>'
37267                 }
37268             ]
37269         });
37270
37271         var close = previewEl.select('button.close', true).first();
37272
37273         close.on('click', this.onRemove, this, file);
37274
37275         file.target = previewEl;
37276
37277         var image = previewEl.select('img', true).first();
37278         
37279         var _this = this;
37280         
37281         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37282         
37283         image.on('click', this.onClick, this, file);
37284         
37285         this.fireEvent('previewrendered', this, file);
37286         
37287         return file;
37288         
37289     },
37290     
37291     onPreviewLoad : function(file, image)
37292     {
37293         if(typeof(file.target) == 'undefined' || !file.target){
37294             return;
37295         }
37296         
37297         var width = image.dom.naturalWidth || image.dom.width;
37298         var height = image.dom.naturalHeight || image.dom.height;
37299         
37300         if(!this.previewResize) {
37301             return;
37302         }
37303         
37304         if(width > height){
37305             file.target.addClass('wide');
37306             return;
37307         }
37308         
37309         file.target.addClass('tall');
37310         return;
37311         
37312     },
37313     
37314     uploadFromSource : function(file, crop)
37315     {
37316         this.xhr = new XMLHttpRequest();
37317         
37318         this.managerEl.createChild({
37319             tag : 'div',
37320             cls : 'roo-document-manager-loading',
37321             cn : [
37322                 {
37323                     tag : 'div',
37324                     tooltip : file.name,
37325                     cls : 'roo-document-manager-thumb',
37326                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37327                 }
37328             ]
37329
37330         });
37331
37332         this.xhr.open(this.method, this.url, true);
37333         
37334         var headers = {
37335             "Accept": "application/json",
37336             "Cache-Control": "no-cache",
37337             "X-Requested-With": "XMLHttpRequest"
37338         };
37339         
37340         for (var headerName in headers) {
37341             var headerValue = headers[headerName];
37342             if (headerValue) {
37343                 this.xhr.setRequestHeader(headerName, headerValue);
37344             }
37345         }
37346         
37347         var _this = this;
37348         
37349         this.xhr.onload = function()
37350         {
37351             _this.xhrOnLoad(_this.xhr);
37352         }
37353         
37354         this.xhr.onerror = function()
37355         {
37356             _this.xhrOnError(_this.xhr);
37357         }
37358         
37359         var formData = new FormData();
37360
37361         formData.append('returnHTML', 'NO');
37362         
37363         formData.append('crop', crop);
37364         
37365         if(typeof(file.filename) != 'undefined'){
37366             formData.append('filename', file.filename);
37367         }
37368         
37369         if(typeof(file.mimetype) != 'undefined'){
37370             formData.append('mimetype', file.mimetype);
37371         }
37372         
37373         Roo.log(formData);
37374         
37375         if(this.fireEvent('prepare', this, formData) != false){
37376             this.xhr.send(formData);
37377         };
37378     }
37379 });
37380
37381 /*
37382 * Licence: LGPL
37383 */
37384
37385 /**
37386  * @class Roo.bootstrap.DocumentViewer
37387  * @extends Roo.bootstrap.Component
37388  * Bootstrap DocumentViewer class
37389  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37390  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37391  * 
37392  * @constructor
37393  * Create a new DocumentViewer
37394  * @param {Object} config The config object
37395  */
37396
37397 Roo.bootstrap.DocumentViewer = function(config){
37398     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37399     
37400     this.addEvents({
37401         /**
37402          * @event initial
37403          * Fire after initEvent
37404          * @param {Roo.bootstrap.DocumentViewer} this
37405          */
37406         "initial" : true,
37407         /**
37408          * @event click
37409          * Fire after click
37410          * @param {Roo.bootstrap.DocumentViewer} this
37411          */
37412         "click" : true,
37413         /**
37414          * @event download
37415          * Fire after download button
37416          * @param {Roo.bootstrap.DocumentViewer} this
37417          */
37418         "download" : true,
37419         /**
37420          * @event trash
37421          * Fire after trash button
37422          * @param {Roo.bootstrap.DocumentViewer} this
37423          */
37424         "trash" : true
37425         
37426     });
37427 };
37428
37429 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37430     
37431     showDownload : true,
37432     
37433     showTrash : true,
37434     
37435     getAutoCreate : function()
37436     {
37437         var cfg = {
37438             tag : 'div',
37439             cls : 'roo-document-viewer',
37440             cn : [
37441                 {
37442                     tag : 'div',
37443                     cls : 'roo-document-viewer-body',
37444                     cn : [
37445                         {
37446                             tag : 'div',
37447                             cls : 'roo-document-viewer-thumb',
37448                             cn : [
37449                                 {
37450                                     tag : 'img',
37451                                     cls : 'roo-document-viewer-image'
37452                                 }
37453                             ]
37454                         }
37455                     ]
37456                 },
37457                 {
37458                     tag : 'div',
37459                     cls : 'roo-document-viewer-footer',
37460                     cn : {
37461                         tag : 'div',
37462                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37463                         cn : [
37464                             {
37465                                 tag : 'div',
37466                                 cls : 'btn-group roo-document-viewer-download',
37467                                 cn : [
37468                                     {
37469                                         tag : 'button',
37470                                         cls : 'btn btn-default',
37471                                         html : '<i class="fa fa-download"></i>'
37472                                     }
37473                                 ]
37474                             },
37475                             {
37476                                 tag : 'div',
37477                                 cls : 'btn-group roo-document-viewer-trash',
37478                                 cn : [
37479                                     {
37480                                         tag : 'button',
37481                                         cls : 'btn btn-default',
37482                                         html : '<i class="fa fa-trash"></i>'
37483                                     }
37484                                 ]
37485                             }
37486                         ]
37487                     }
37488                 }
37489             ]
37490         };
37491         
37492         return cfg;
37493     },
37494     
37495     initEvents : function()
37496     {
37497         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37498         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37499         
37500         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37501         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37502         
37503         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37504         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37505         
37506         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37507         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37508         
37509         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37510         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37511         
37512         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37513         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37514         
37515         this.bodyEl.on('click', this.onClick, this);
37516         this.downloadBtn.on('click', this.onDownload, this);
37517         this.trashBtn.on('click', this.onTrash, this);
37518         
37519         this.downloadBtn.hide();
37520         this.trashBtn.hide();
37521         
37522         if(this.showDownload){
37523             this.downloadBtn.show();
37524         }
37525         
37526         if(this.showTrash){
37527             this.trashBtn.show();
37528         }
37529         
37530         if(!this.showDownload && !this.showTrash) {
37531             this.footerEl.hide();
37532         }
37533         
37534     },
37535     
37536     initial : function()
37537     {
37538         this.fireEvent('initial', this);
37539         
37540     },
37541     
37542     onClick : function(e)
37543     {
37544         e.preventDefault();
37545         
37546         this.fireEvent('click', this);
37547     },
37548     
37549     onDownload : function(e)
37550     {
37551         e.preventDefault();
37552         
37553         this.fireEvent('download', this);
37554     },
37555     
37556     onTrash : function(e)
37557     {
37558         e.preventDefault();
37559         
37560         this.fireEvent('trash', this);
37561     }
37562     
37563 });
37564 /*
37565  * - LGPL
37566  *
37567  * FieldLabel
37568  * 
37569  */
37570
37571 /**
37572  * @class Roo.bootstrap.form.FieldLabel
37573  * @extends Roo.bootstrap.Component
37574  * Bootstrap FieldLabel class
37575  * @cfg {String} html contents of the element
37576  * @cfg {String} tag tag of the element default label
37577  * @cfg {String} cls class of the element
37578  * @cfg {String} target label target 
37579  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37580  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37581  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37582  * @cfg {String} iconTooltip default "This field is required"
37583  * @cfg {String} indicatorpos (left|right) default left
37584  * 
37585  * @constructor
37586  * Create a new FieldLabel
37587  * @param {Object} config The config object
37588  */
37589
37590 Roo.bootstrap.form.FieldLabel = function(config){
37591     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37592     
37593     this.addEvents({
37594             /**
37595              * @event invalid
37596              * Fires after the field has been marked as invalid.
37597              * @param {Roo.form.FieldLabel} this
37598              * @param {String} msg The validation message
37599              */
37600             invalid : true,
37601             /**
37602              * @event valid
37603              * Fires after the field has been validated with no errors.
37604              * @param {Roo.form.FieldLabel} this
37605              */
37606             valid : true
37607         });
37608 };
37609
37610 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37611     
37612     tag: 'label',
37613     cls: '',
37614     html: '',
37615     target: '',
37616     allowBlank : true,
37617     invalidClass : 'has-warning',
37618     validClass : 'has-success',
37619     iconTooltip : 'This field is required',
37620     indicatorpos : 'left',
37621     
37622     getAutoCreate : function(){
37623         
37624         var cls = "";
37625         if (!this.allowBlank) {
37626             cls  = "visible";
37627         }
37628         
37629         var cfg = {
37630             tag : this.tag,
37631             cls : 'roo-bootstrap-field-label ' + this.cls,
37632             for : this.target,
37633             cn : [
37634                 {
37635                     tag : 'i',
37636                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37637                     tooltip : this.iconTooltip
37638                 },
37639                 {
37640                     tag : 'span',
37641                     html : this.html
37642                 }
37643             ] 
37644         };
37645         
37646         if(this.indicatorpos == 'right'){
37647             var cfg = {
37648                 tag : this.tag,
37649                 cls : 'roo-bootstrap-field-label ' + this.cls,
37650                 for : this.target,
37651                 cn : [
37652                     {
37653                         tag : 'span',
37654                         html : this.html
37655                     },
37656                     {
37657                         tag : 'i',
37658                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37659                         tooltip : this.iconTooltip
37660                     }
37661                 ] 
37662             };
37663         }
37664         
37665         return cfg;
37666     },
37667     
37668     initEvents: function() 
37669     {
37670         Roo.bootstrap.Element.superclass.initEvents.call(this);
37671         
37672         this.indicator = this.indicatorEl();
37673         
37674         if(this.indicator){
37675             this.indicator.removeClass('visible');
37676             this.indicator.addClass('invisible');
37677         }
37678         
37679         Roo.bootstrap.form.FieldLabel.register(this);
37680     },
37681     
37682     indicatorEl : function()
37683     {
37684         var indicator = this.el.select('i.roo-required-indicator',true).first();
37685         
37686         if(!indicator){
37687             return false;
37688         }
37689         
37690         return indicator;
37691         
37692     },
37693     
37694     /**
37695      * Mark this field as valid
37696      */
37697     markValid : function()
37698     {
37699         if(this.indicator){
37700             this.indicator.removeClass('visible');
37701             this.indicator.addClass('invisible');
37702         }
37703         if (Roo.bootstrap.version == 3) {
37704             this.el.removeClass(this.invalidClass);
37705             this.el.addClass(this.validClass);
37706         } else {
37707             this.el.removeClass('is-invalid');
37708             this.el.addClass('is-valid');
37709         }
37710         
37711         
37712         this.fireEvent('valid', this);
37713     },
37714     
37715     /**
37716      * Mark this field as invalid
37717      * @param {String} msg The validation message
37718      */
37719     markInvalid : function(msg)
37720     {
37721         if(this.indicator){
37722             this.indicator.removeClass('invisible');
37723             this.indicator.addClass('visible');
37724         }
37725           if (Roo.bootstrap.version == 3) {
37726             this.el.removeClass(this.validClass);
37727             this.el.addClass(this.invalidClass);
37728         } else {
37729             this.el.removeClass('is-valid');
37730             this.el.addClass('is-invalid');
37731         }
37732         
37733         
37734         this.fireEvent('invalid', this, msg);
37735     }
37736     
37737    
37738 });
37739
37740 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37741     
37742     groups: {},
37743     
37744      /**
37745     * register a FieldLabel Group
37746     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37747     */
37748     register : function(label)
37749     {
37750         if(this.groups.hasOwnProperty(label.target)){
37751             return;
37752         }
37753      
37754         this.groups[label.target] = label;
37755         
37756     },
37757     /**
37758     * fetch a FieldLabel Group based on the target
37759     * @param {string} target
37760     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37761     */
37762     get: function(target) {
37763         if (typeof(this.groups[target]) == 'undefined') {
37764             return false;
37765         }
37766         
37767         return this.groups[target] ;
37768     }
37769 });
37770
37771  
37772
37773  /*
37774  * - LGPL
37775  *
37776  * page DateSplitField.
37777  * 
37778  */
37779
37780
37781 /**
37782  * @class Roo.bootstrap.form.DateSplitField
37783  * @extends Roo.bootstrap.Component
37784  * Bootstrap DateSplitField class
37785  * @cfg {string} fieldLabel - the label associated
37786  * @cfg {Number} labelWidth set the width of label (0-12)
37787  * @cfg {String} labelAlign (top|left)
37788  * @cfg {Boolean} dayAllowBlank (true|false) default false
37789  * @cfg {Boolean} monthAllowBlank (true|false) default false
37790  * @cfg {Boolean} yearAllowBlank (true|false) default false
37791  * @cfg {string} dayPlaceholder 
37792  * @cfg {string} monthPlaceholder
37793  * @cfg {string} yearPlaceholder
37794  * @cfg {string} dayFormat default 'd'
37795  * @cfg {string} monthFormat default 'm'
37796  * @cfg {string} yearFormat default 'Y'
37797  * @cfg {Number} labellg set the width of label (1-12)
37798  * @cfg {Number} labelmd set the width of label (1-12)
37799  * @cfg {Number} labelsm set the width of label (1-12)
37800  * @cfg {Number} labelxs set the width of label (1-12)
37801
37802  *     
37803  * @constructor
37804  * Create a new DateSplitField
37805  * @param {Object} config The config object
37806  */
37807
37808 Roo.bootstrap.form.DateSplitField = function(config){
37809     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37810     
37811     this.addEvents({
37812         // raw events
37813          /**
37814          * @event years
37815          * getting the data of years
37816          * @param {Roo.bootstrap.form.DateSplitField} this
37817          * @param {Object} years
37818          */
37819         "years" : true,
37820         /**
37821          * @event days
37822          * getting the data of days
37823          * @param {Roo.bootstrap.form.DateSplitField} this
37824          * @param {Object} days
37825          */
37826         "days" : true,
37827         /**
37828          * @event invalid
37829          * Fires after the field has been marked as invalid.
37830          * @param {Roo.form.Field} this
37831          * @param {String} msg The validation message
37832          */
37833         invalid : true,
37834        /**
37835          * @event valid
37836          * Fires after the field has been validated with no errors.
37837          * @param {Roo.form.Field} this
37838          */
37839         valid : true
37840     });
37841 };
37842
37843 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37844     
37845     fieldLabel : '',
37846     labelAlign : 'top',
37847     labelWidth : 3,
37848     dayAllowBlank : false,
37849     monthAllowBlank : false,
37850     yearAllowBlank : false,
37851     dayPlaceholder : '',
37852     monthPlaceholder : '',
37853     yearPlaceholder : '',
37854     dayFormat : 'd',
37855     monthFormat : 'm',
37856     yearFormat : 'Y',
37857     isFormField : true,
37858     labellg : 0,
37859     labelmd : 0,
37860     labelsm : 0,
37861     labelxs : 0,
37862     
37863     getAutoCreate : function()
37864     {
37865         var cfg = {
37866             tag : 'div',
37867             cls : 'row roo-date-split-field-group',
37868             cn : [
37869                 {
37870                     tag : 'input',
37871                     type : 'hidden',
37872                     cls : 'form-hidden-field roo-date-split-field-group-value',
37873                     name : this.name
37874                 }
37875             ]
37876         };
37877         
37878         var labelCls = 'col-md-12';
37879         var contentCls = 'col-md-4';
37880         
37881         if(this.fieldLabel){
37882             
37883             var label = {
37884                 tag : 'div',
37885                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37886                 cn : [
37887                     {
37888                         tag : 'label',
37889                         html : this.fieldLabel
37890                     }
37891                 ]
37892             };
37893             
37894             if(this.labelAlign == 'left'){
37895             
37896                 if(this.labelWidth > 12){
37897                     label.style = "width: " + this.labelWidth + 'px';
37898                 }
37899
37900                 if(this.labelWidth < 13 && this.labelmd == 0){
37901                     this.labelmd = this.labelWidth;
37902                 }
37903
37904                 if(this.labellg > 0){
37905                     labelCls = ' col-lg-' + this.labellg;
37906                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37907                 }
37908
37909                 if(this.labelmd > 0){
37910                     labelCls = ' col-md-' + this.labelmd;
37911                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37912                 }
37913
37914                 if(this.labelsm > 0){
37915                     labelCls = ' col-sm-' + this.labelsm;
37916                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37917                 }
37918
37919                 if(this.labelxs > 0){
37920                     labelCls = ' col-xs-' + this.labelxs;
37921                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37922                 }
37923             }
37924             
37925             label.cls += ' ' + labelCls;
37926             
37927             cfg.cn.push(label);
37928         }
37929         
37930         Roo.each(['day', 'month', 'year'], function(t){
37931             cfg.cn.push({
37932                 tag : 'div',
37933                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37934             });
37935         }, this);
37936         
37937         return cfg;
37938     },
37939     
37940     inputEl: function ()
37941     {
37942         return this.el.select('.roo-date-split-field-group-value', true).first();
37943     },
37944     
37945     onRender : function(ct, position) 
37946     {
37947         var _this = this;
37948         
37949         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37950         
37951         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37952         
37953         this.dayField = new Roo.bootstrap.form.ComboBox({
37954             allowBlank : this.dayAllowBlank,
37955             alwaysQuery : true,
37956             displayField : 'value',
37957             editable : false,
37958             fieldLabel : '',
37959             forceSelection : true,
37960             mode : 'local',
37961             placeholder : this.dayPlaceholder,
37962             selectOnFocus : true,
37963             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37964             triggerAction : 'all',
37965             typeAhead : true,
37966             valueField : 'value',
37967             store : new Roo.data.SimpleStore({
37968                 data : (function() {    
37969                     var days = [];
37970                     _this.fireEvent('days', _this, days);
37971                     return days;
37972                 })(),
37973                 fields : [ 'value' ]
37974             }),
37975             listeners : {
37976                 select : function (_self, record, index)
37977                 {
37978                     _this.setValue(_this.getValue());
37979                 }
37980             }
37981         });
37982
37983         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37984         
37985         this.monthField = new Roo.bootstrap.form.MonthField({
37986             after : '<i class=\"fa fa-calendar\"></i>',
37987             allowBlank : this.monthAllowBlank,
37988             placeholder : this.monthPlaceholder,
37989             readOnly : true,
37990             listeners : {
37991                 render : function (_self)
37992                 {
37993                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
37994                         e.preventDefault();
37995                         _self.focus();
37996                     });
37997                 },
37998                 select : function (_self, oldvalue, newvalue)
37999                 {
38000                     _this.setValue(_this.getValue());
38001                 }
38002             }
38003         });
38004         
38005         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38006         
38007         this.yearField = new Roo.bootstrap.form.ComboBox({
38008             allowBlank : this.yearAllowBlank,
38009             alwaysQuery : true,
38010             displayField : 'value',
38011             editable : false,
38012             fieldLabel : '',
38013             forceSelection : true,
38014             mode : 'local',
38015             placeholder : this.yearPlaceholder,
38016             selectOnFocus : true,
38017             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38018             triggerAction : 'all',
38019             typeAhead : true,
38020             valueField : 'value',
38021             store : new Roo.data.SimpleStore({
38022                 data : (function() {
38023                     var years = [];
38024                     _this.fireEvent('years', _this, years);
38025                     return years;
38026                 })(),
38027                 fields : [ 'value' ]
38028             }),
38029             listeners : {
38030                 select : function (_self, record, index)
38031                 {
38032                     _this.setValue(_this.getValue());
38033                 }
38034             }
38035         });
38036
38037         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38038     },
38039     
38040     setValue : function(v, format)
38041     {
38042         this.inputEl.dom.value = v;
38043         
38044         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38045         
38046         var d = Date.parseDate(v, f);
38047         
38048         if(!d){
38049             this.validate();
38050             return;
38051         }
38052         
38053         this.setDay(d.format(this.dayFormat));
38054         this.setMonth(d.format(this.monthFormat));
38055         this.setYear(d.format(this.yearFormat));
38056         
38057         this.validate();
38058         
38059         return;
38060     },
38061     
38062     setDay : function(v)
38063     {
38064         this.dayField.setValue(v);
38065         this.inputEl.dom.value = this.getValue();
38066         this.validate();
38067         return;
38068     },
38069     
38070     setMonth : function(v)
38071     {
38072         this.monthField.setValue(v, true);
38073         this.inputEl.dom.value = this.getValue();
38074         this.validate();
38075         return;
38076     },
38077     
38078     setYear : function(v)
38079     {
38080         this.yearField.setValue(v);
38081         this.inputEl.dom.value = this.getValue();
38082         this.validate();
38083         return;
38084     },
38085     
38086     getDay : function()
38087     {
38088         return this.dayField.getValue();
38089     },
38090     
38091     getMonth : function()
38092     {
38093         return this.monthField.getValue();
38094     },
38095     
38096     getYear : function()
38097     {
38098         return this.yearField.getValue();
38099     },
38100     
38101     getValue : function()
38102     {
38103         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38104         
38105         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38106         
38107         return date;
38108     },
38109     
38110     reset : function()
38111     {
38112         this.setDay('');
38113         this.setMonth('');
38114         this.setYear('');
38115         this.inputEl.dom.value = '';
38116         this.validate();
38117         return;
38118     },
38119     
38120     validate : function()
38121     {
38122         var d = this.dayField.validate();
38123         var m = this.monthField.validate();
38124         var y = this.yearField.validate();
38125         
38126         var valid = true;
38127         
38128         if(
38129                 (!this.dayAllowBlank && !d) ||
38130                 (!this.monthAllowBlank && !m) ||
38131                 (!this.yearAllowBlank && !y)
38132         ){
38133             valid = false;
38134         }
38135         
38136         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38137             return valid;
38138         }
38139         
38140         if(valid){
38141             this.markValid();
38142             return valid;
38143         }
38144         
38145         this.markInvalid();
38146         
38147         return valid;
38148     },
38149     
38150     markValid : function()
38151     {
38152         
38153         var label = this.el.select('label', true).first();
38154         var icon = this.el.select('i.fa-star', true).first();
38155
38156         if(label && icon){
38157             icon.remove();
38158         }
38159         
38160         this.fireEvent('valid', this);
38161     },
38162     
38163      /**
38164      * Mark this field as invalid
38165      * @param {String} msg The validation message
38166      */
38167     markInvalid : function(msg)
38168     {
38169         
38170         var label = this.el.select('label', true).first();
38171         var icon = this.el.select('i.fa-star', true).first();
38172
38173         if(label && !icon){
38174             this.el.select('.roo-date-split-field-label', true).createChild({
38175                 tag : 'i',
38176                 cls : 'text-danger fa fa-lg fa-star',
38177                 tooltip : 'This field is required',
38178                 style : 'margin-right:5px;'
38179             }, label, true);
38180         }
38181         
38182         this.fireEvent('invalid', this, msg);
38183     },
38184     
38185     clearInvalid : function()
38186     {
38187         var label = this.el.select('label', true).first();
38188         var icon = this.el.select('i.fa-star', true).first();
38189
38190         if(label && icon){
38191             icon.remove();
38192         }
38193         
38194         this.fireEvent('valid', this);
38195     },
38196     
38197     getName: function()
38198     {
38199         return this.name;
38200     }
38201     
38202 });
38203
38204  
38205
38206 /**
38207  * @class Roo.bootstrap.LayoutMasonry
38208  * @extends Roo.bootstrap.Component
38209  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38210  * Bootstrap Layout Masonry class
38211  *
38212  * This is based on 
38213  * http://masonry.desandro.com
38214  *
38215  * The idea is to render all the bricks based on vertical width...
38216  *
38217  * The original code extends 'outlayer' - we might need to use that....
38218
38219  * @constructor
38220  * Create a new Element
38221  * @param {Object} config The config object
38222  */
38223
38224 Roo.bootstrap.LayoutMasonry = function(config){
38225     
38226     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38227     
38228     this.bricks = [];
38229     
38230     Roo.bootstrap.LayoutMasonry.register(this);
38231     
38232     this.addEvents({
38233         // raw events
38234         /**
38235          * @event layout
38236          * Fire after layout the items
38237          * @param {Roo.bootstrap.LayoutMasonry} this
38238          * @param {Roo.EventObject} e
38239          */
38240         "layout" : true
38241     });
38242     
38243 };
38244
38245 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38246     
38247     /**
38248      * @cfg {Boolean} isLayoutInstant = no animation?
38249      */   
38250     isLayoutInstant : false, // needed?
38251    
38252     /**
38253      * @cfg {Number} boxWidth  width of the columns
38254      */   
38255     boxWidth : 450,
38256     
38257       /**
38258      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38259      */   
38260     boxHeight : 0,
38261     
38262     /**
38263      * @cfg {Number} padWidth padding below box..
38264      */   
38265     padWidth : 10, 
38266     
38267     /**
38268      * @cfg {Number} gutter gutter width..
38269      */   
38270     gutter : 10,
38271     
38272      /**
38273      * @cfg {Number} maxCols maximum number of columns
38274      */   
38275     
38276     maxCols: 0,
38277     
38278     /**
38279      * @cfg {Boolean} isAutoInitial defalut true
38280      */   
38281     isAutoInitial : true, 
38282     
38283     containerWidth: 0,
38284     
38285     /**
38286      * @cfg {Boolean} isHorizontal defalut false
38287      */   
38288     isHorizontal : false, 
38289
38290     currentSize : null,
38291     
38292     tag: 'div',
38293     
38294     cls: '',
38295     
38296     bricks: null, //CompositeElement
38297     
38298     cols : 1,
38299     
38300     _isLayoutInited : false,
38301     
38302 //    isAlternative : false, // only use for vertical layout...
38303     
38304     /**
38305      * @cfg {Number} alternativePadWidth padding below box..
38306      */   
38307     alternativePadWidth : 50,
38308     
38309     selectedBrick : [],
38310     
38311     getAutoCreate : function(){
38312         
38313         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38314         
38315         var cfg = {
38316             tag: this.tag,
38317             cls: 'blog-masonary-wrapper ' + this.cls,
38318             cn : {
38319                 cls : 'mas-boxes masonary'
38320             }
38321         };
38322         
38323         return cfg;
38324     },
38325     
38326     getChildContainer: function( )
38327     {
38328         if (this.boxesEl) {
38329             return this.boxesEl;
38330         }
38331         
38332         this.boxesEl = this.el.select('.mas-boxes').first();
38333         
38334         return this.boxesEl;
38335     },
38336     
38337     
38338     initEvents : function()
38339     {
38340         var _this = this;
38341         
38342         if(this.isAutoInitial){
38343             Roo.log('hook children rendered');
38344             this.on('childrenrendered', function() {
38345                 Roo.log('children rendered');
38346                 _this.initial();
38347             } ,this);
38348         }
38349     },
38350     
38351     initial : function()
38352     {
38353         this.selectedBrick = [];
38354         
38355         this.currentSize = this.el.getBox(true);
38356         
38357         Roo.EventManager.onWindowResize(this.resize, this); 
38358
38359         if(!this.isAutoInitial){
38360             this.layout();
38361             return;
38362         }
38363         
38364         this.layout();
38365         
38366         return;
38367         //this.layout.defer(500,this);
38368         
38369     },
38370     
38371     resize : function()
38372     {
38373         var cs = this.el.getBox(true);
38374         
38375         if (
38376                 this.currentSize.width == cs.width && 
38377                 this.currentSize.x == cs.x && 
38378                 this.currentSize.height == cs.height && 
38379                 this.currentSize.y == cs.y 
38380         ) {
38381             Roo.log("no change in with or X or Y");
38382             return;
38383         }
38384         
38385         this.currentSize = cs;
38386         
38387         this.layout();
38388         
38389     },
38390     
38391     layout : function()
38392     {   
38393         this._resetLayout();
38394         
38395         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38396         
38397         this.layoutItems( isInstant );
38398       
38399         this._isLayoutInited = true;
38400         
38401         this.fireEvent('layout', this);
38402         
38403     },
38404     
38405     _resetLayout : function()
38406     {
38407         if(this.isHorizontal){
38408             this.horizontalMeasureColumns();
38409             return;
38410         }
38411         
38412         this.verticalMeasureColumns();
38413         
38414     },
38415     
38416     verticalMeasureColumns : function()
38417     {
38418         this.getContainerWidth();
38419         
38420 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38421 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38422 //            return;
38423 //        }
38424         
38425         var boxWidth = this.boxWidth + this.padWidth;
38426         
38427         if(this.containerWidth < this.boxWidth){
38428             boxWidth = this.containerWidth
38429         }
38430         
38431         var containerWidth = this.containerWidth;
38432         
38433         var cols = Math.floor(containerWidth / boxWidth);
38434         
38435         this.cols = Math.max( cols, 1 );
38436         
38437         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38438         
38439         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38440         
38441         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38442         
38443         this.colWidth = boxWidth + avail - this.padWidth;
38444         
38445         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38446         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38447     },
38448     
38449     horizontalMeasureColumns : function()
38450     {
38451         this.getContainerWidth();
38452         
38453         var boxWidth = this.boxWidth;
38454         
38455         if(this.containerWidth < boxWidth){
38456             boxWidth = this.containerWidth;
38457         }
38458         
38459         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38460         
38461         this.el.setHeight(boxWidth);
38462         
38463     },
38464     
38465     getContainerWidth : function()
38466     {
38467         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38468     },
38469     
38470     layoutItems : function( isInstant )
38471     {
38472         Roo.log(this.bricks);
38473         
38474         var items = Roo.apply([], this.bricks);
38475         
38476         if(this.isHorizontal){
38477             this._horizontalLayoutItems( items , isInstant );
38478             return;
38479         }
38480         
38481 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38482 //            this._verticalAlternativeLayoutItems( items , isInstant );
38483 //            return;
38484 //        }
38485         
38486         this._verticalLayoutItems( items , isInstant );
38487         
38488     },
38489     
38490     _verticalLayoutItems : function ( items , isInstant)
38491     {
38492         if ( !items || !items.length ) {
38493             return;
38494         }
38495         
38496         var standard = [
38497             ['xs', 'xs', 'xs', 'tall'],
38498             ['xs', 'xs', 'tall'],
38499             ['xs', 'xs', 'sm'],
38500             ['xs', 'xs', 'xs'],
38501             ['xs', 'tall'],
38502             ['xs', 'sm'],
38503             ['xs', 'xs'],
38504             ['xs'],
38505             
38506             ['sm', 'xs', 'xs'],
38507             ['sm', 'xs'],
38508             ['sm'],
38509             
38510             ['tall', 'xs', 'xs', 'xs'],
38511             ['tall', 'xs', 'xs'],
38512             ['tall', 'xs'],
38513             ['tall']
38514             
38515         ];
38516         
38517         var queue = [];
38518         
38519         var boxes = [];
38520         
38521         var box = [];
38522         
38523         Roo.each(items, function(item, k){
38524             
38525             switch (item.size) {
38526                 // these layouts take up a full box,
38527                 case 'md' :
38528                 case 'md-left' :
38529                 case 'md-right' :
38530                 case 'wide' :
38531                     
38532                     if(box.length){
38533                         boxes.push(box);
38534                         box = [];
38535                     }
38536                     
38537                     boxes.push([item]);
38538                     
38539                     break;
38540                     
38541                 case 'xs' :
38542                 case 'sm' :
38543                 case 'tall' :
38544                     
38545                     box.push(item);
38546                     
38547                     break;
38548                 default :
38549                     break;
38550                     
38551             }
38552             
38553         }, this);
38554         
38555         if(box.length){
38556             boxes.push(box);
38557             box = [];
38558         }
38559         
38560         var filterPattern = function(box, length)
38561         {
38562             if(!box.length){
38563                 return;
38564             }
38565             
38566             var match = false;
38567             
38568             var pattern = box.slice(0, length);
38569             
38570             var format = [];
38571             
38572             Roo.each(pattern, function(i){
38573                 format.push(i.size);
38574             }, this);
38575             
38576             Roo.each(standard, function(s){
38577                 
38578                 if(String(s) != String(format)){
38579                     return;
38580                 }
38581                 
38582                 match = true;
38583                 return false;
38584                 
38585             }, this);
38586             
38587             if(!match && length == 1){
38588                 return;
38589             }
38590             
38591             if(!match){
38592                 filterPattern(box, length - 1);
38593                 return;
38594             }
38595                 
38596             queue.push(pattern);
38597
38598             box = box.slice(length, box.length);
38599
38600             filterPattern(box, 4);
38601
38602             return;
38603             
38604         }
38605         
38606         Roo.each(boxes, function(box, k){
38607             
38608             if(!box.length){
38609                 return;
38610             }
38611             
38612             if(box.length == 1){
38613                 queue.push(box);
38614                 return;
38615             }
38616             
38617             filterPattern(box, 4);
38618             
38619         }, this);
38620         
38621         this._processVerticalLayoutQueue( queue, isInstant );
38622         
38623     },
38624     
38625 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38626 //    {
38627 //        if ( !items || !items.length ) {
38628 //            return;
38629 //        }
38630 //
38631 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38632 //        
38633 //    },
38634     
38635     _horizontalLayoutItems : function ( items , isInstant)
38636     {
38637         if ( !items || !items.length || items.length < 3) {
38638             return;
38639         }
38640         
38641         items.reverse();
38642         
38643         var eItems = items.slice(0, 3);
38644         
38645         items = items.slice(3, items.length);
38646         
38647         var standard = [
38648             ['xs', 'xs', 'xs', 'wide'],
38649             ['xs', 'xs', 'wide'],
38650             ['xs', 'xs', 'sm'],
38651             ['xs', 'xs', 'xs'],
38652             ['xs', 'wide'],
38653             ['xs', 'sm'],
38654             ['xs', 'xs'],
38655             ['xs'],
38656             
38657             ['sm', 'xs', 'xs'],
38658             ['sm', 'xs'],
38659             ['sm'],
38660             
38661             ['wide', 'xs', 'xs', 'xs'],
38662             ['wide', 'xs', 'xs'],
38663             ['wide', 'xs'],
38664             ['wide'],
38665             
38666             ['wide-thin']
38667         ];
38668         
38669         var queue = [];
38670         
38671         var boxes = [];
38672         
38673         var box = [];
38674         
38675         Roo.each(items, function(item, k){
38676             
38677             switch (item.size) {
38678                 case 'md' :
38679                 case 'md-left' :
38680                 case 'md-right' :
38681                 case 'tall' :
38682                     
38683                     if(box.length){
38684                         boxes.push(box);
38685                         box = [];
38686                     }
38687                     
38688                     boxes.push([item]);
38689                     
38690                     break;
38691                     
38692                 case 'xs' :
38693                 case 'sm' :
38694                 case 'wide' :
38695                 case 'wide-thin' :
38696                     
38697                     box.push(item);
38698                     
38699                     break;
38700                 default :
38701                     break;
38702                     
38703             }
38704             
38705         }, this);
38706         
38707         if(box.length){
38708             boxes.push(box);
38709             box = [];
38710         }
38711         
38712         var filterPattern = function(box, length)
38713         {
38714             if(!box.length){
38715                 return;
38716             }
38717             
38718             var match = false;
38719             
38720             var pattern = box.slice(0, length);
38721             
38722             var format = [];
38723             
38724             Roo.each(pattern, function(i){
38725                 format.push(i.size);
38726             }, this);
38727             
38728             Roo.each(standard, function(s){
38729                 
38730                 if(String(s) != String(format)){
38731                     return;
38732                 }
38733                 
38734                 match = true;
38735                 return false;
38736                 
38737             }, this);
38738             
38739             if(!match && length == 1){
38740                 return;
38741             }
38742             
38743             if(!match){
38744                 filterPattern(box, length - 1);
38745                 return;
38746             }
38747                 
38748             queue.push(pattern);
38749
38750             box = box.slice(length, box.length);
38751
38752             filterPattern(box, 4);
38753
38754             return;
38755             
38756         }
38757         
38758         Roo.each(boxes, function(box, k){
38759             
38760             if(!box.length){
38761                 return;
38762             }
38763             
38764             if(box.length == 1){
38765                 queue.push(box);
38766                 return;
38767             }
38768             
38769             filterPattern(box, 4);
38770             
38771         }, this);
38772         
38773         
38774         var prune = [];
38775         
38776         var pos = this.el.getBox(true);
38777         
38778         var minX = pos.x;
38779         
38780         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38781         
38782         var hit_end = false;
38783         
38784         Roo.each(queue, function(box){
38785             
38786             if(hit_end){
38787                 
38788                 Roo.each(box, function(b){
38789                 
38790                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38791                     b.el.hide();
38792
38793                 }, this);
38794
38795                 return;
38796             }
38797             
38798             var mx = 0;
38799             
38800             Roo.each(box, function(b){
38801                 
38802                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38803                 b.el.show();
38804
38805                 mx = Math.max(mx, b.x);
38806                 
38807             }, this);
38808             
38809             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38810             
38811             if(maxX < minX){
38812                 
38813                 Roo.each(box, function(b){
38814                 
38815                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38816                     b.el.hide();
38817                     
38818                 }, this);
38819                 
38820                 hit_end = true;
38821                 
38822                 return;
38823             }
38824             
38825             prune.push(box);
38826             
38827         }, this);
38828         
38829         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38830     },
38831     
38832     /** Sets position of item in DOM
38833     * @param {Element} item
38834     * @param {Number} x - horizontal position
38835     * @param {Number} y - vertical position
38836     * @param {Boolean} isInstant - disables transitions
38837     */
38838     _processVerticalLayoutQueue : function( queue, isInstant )
38839     {
38840         var pos = this.el.getBox(true);
38841         var x = pos.x;
38842         var y = pos.y;
38843         var maxY = [];
38844         
38845         for (var i = 0; i < this.cols; i++){
38846             maxY[i] = pos.y;
38847         }
38848         
38849         Roo.each(queue, function(box, k){
38850             
38851             var col = k % this.cols;
38852             
38853             Roo.each(box, function(b,kk){
38854                 
38855                 b.el.position('absolute');
38856                 
38857                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38858                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38859                 
38860                 if(b.size == 'md-left' || b.size == 'md-right'){
38861                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38862                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38863                 }
38864                 
38865                 b.el.setWidth(width);
38866                 b.el.setHeight(height);
38867                 // iframe?
38868                 b.el.select('iframe',true).setSize(width,height);
38869                 
38870             }, this);
38871             
38872             for (var i = 0; i < this.cols; i++){
38873                 
38874                 if(maxY[i] < maxY[col]){
38875                     col = i;
38876                     continue;
38877                 }
38878                 
38879                 col = Math.min(col, i);
38880                 
38881             }
38882             
38883             x = pos.x + col * (this.colWidth + this.padWidth);
38884             
38885             y = maxY[col];
38886             
38887             var positions = [];
38888             
38889             switch (box.length){
38890                 case 1 :
38891                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38892                     break;
38893                 case 2 :
38894                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38895                     break;
38896                 case 3 :
38897                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38898                     break;
38899                 case 4 :
38900                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38901                     break;
38902                 default :
38903                     break;
38904             }
38905             
38906             Roo.each(box, function(b,kk){
38907                 
38908                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38909                 
38910                 var sz = b.el.getSize();
38911                 
38912                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38913                 
38914             }, this);
38915             
38916         }, this);
38917         
38918         var mY = 0;
38919         
38920         for (var i = 0; i < this.cols; i++){
38921             mY = Math.max(mY, maxY[i]);
38922         }
38923         
38924         this.el.setHeight(mY - pos.y);
38925         
38926     },
38927     
38928 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38929 //    {
38930 //        var pos = this.el.getBox(true);
38931 //        var x = pos.x;
38932 //        var y = pos.y;
38933 //        var maxX = pos.right;
38934 //        
38935 //        var maxHeight = 0;
38936 //        
38937 //        Roo.each(items, function(item, k){
38938 //            
38939 //            var c = k % 2;
38940 //            
38941 //            item.el.position('absolute');
38942 //                
38943 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38944 //
38945 //            item.el.setWidth(width);
38946 //
38947 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38948 //
38949 //            item.el.setHeight(height);
38950 //            
38951 //            if(c == 0){
38952 //                item.el.setXY([x, y], isInstant ? false : true);
38953 //            } else {
38954 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38955 //            }
38956 //            
38957 //            y = y + height + this.alternativePadWidth;
38958 //            
38959 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38960 //            
38961 //        }, this);
38962 //        
38963 //        this.el.setHeight(maxHeight);
38964 //        
38965 //    },
38966     
38967     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38968     {
38969         var pos = this.el.getBox(true);
38970         
38971         var minX = pos.x;
38972         var minY = pos.y;
38973         
38974         var maxX = pos.right;
38975         
38976         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38977         
38978         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38979         
38980         Roo.each(queue, function(box, k){
38981             
38982             Roo.each(box, function(b, kk){
38983                 
38984                 b.el.position('absolute');
38985                 
38986                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38987                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38988                 
38989                 if(b.size == 'md-left' || b.size == 'md-right'){
38990                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38991                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38992                 }
38993                 
38994                 b.el.setWidth(width);
38995                 b.el.setHeight(height);
38996                 
38997             }, this);
38998             
38999             if(!box.length){
39000                 return;
39001             }
39002             
39003             var positions = [];
39004             
39005             switch (box.length){
39006                 case 1 :
39007                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39008                     break;
39009                 case 2 :
39010                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39011                     break;
39012                 case 3 :
39013                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39014                     break;
39015                 case 4 :
39016                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39017                     break;
39018                 default :
39019                     break;
39020             }
39021             
39022             Roo.each(box, function(b,kk){
39023                 
39024                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39025                 
39026                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39027                 
39028             }, this);
39029             
39030         }, this);
39031         
39032     },
39033     
39034     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39035     {
39036         Roo.each(eItems, function(b,k){
39037             
39038             b.size = (k == 0) ? 'sm' : 'xs';
39039             b.x = (k == 0) ? 2 : 1;
39040             b.y = (k == 0) ? 2 : 1;
39041             
39042             b.el.position('absolute');
39043             
39044             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39045                 
39046             b.el.setWidth(width);
39047             
39048             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39049             
39050             b.el.setHeight(height);
39051             
39052         }, this);
39053
39054         var positions = [];
39055         
39056         positions.push({
39057             x : maxX - this.unitWidth * 2 - this.gutter,
39058             y : minY
39059         });
39060         
39061         positions.push({
39062             x : maxX - this.unitWidth,
39063             y : minY + (this.unitWidth + this.gutter) * 2
39064         });
39065         
39066         positions.push({
39067             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39068             y : minY
39069         });
39070         
39071         Roo.each(eItems, function(b,k){
39072             
39073             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39074
39075         }, this);
39076         
39077     },
39078     
39079     getVerticalOneBoxColPositions : function(x, y, box)
39080     {
39081         var pos = [];
39082         
39083         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39084         
39085         if(box[0].size == 'md-left'){
39086             rand = 0;
39087         }
39088         
39089         if(box[0].size == 'md-right'){
39090             rand = 1;
39091         }
39092         
39093         pos.push({
39094             x : x + (this.unitWidth + this.gutter) * rand,
39095             y : y
39096         });
39097         
39098         return pos;
39099     },
39100     
39101     getVerticalTwoBoxColPositions : function(x, y, box)
39102     {
39103         var pos = [];
39104         
39105         if(box[0].size == 'xs'){
39106             
39107             pos.push({
39108                 x : x,
39109                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39110             });
39111
39112             pos.push({
39113                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39114                 y : y
39115             });
39116             
39117             return pos;
39118             
39119         }
39120         
39121         pos.push({
39122             x : x,
39123             y : y
39124         });
39125
39126         pos.push({
39127             x : x + (this.unitWidth + this.gutter) * 2,
39128             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39129         });
39130         
39131         return pos;
39132         
39133     },
39134     
39135     getVerticalThreeBoxColPositions : function(x, y, box)
39136     {
39137         var pos = [];
39138         
39139         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39140             
39141             pos.push({
39142                 x : x,
39143                 y : y
39144             });
39145
39146             pos.push({
39147                 x : x + (this.unitWidth + this.gutter) * 1,
39148                 y : y
39149             });
39150             
39151             pos.push({
39152                 x : x + (this.unitWidth + this.gutter) * 2,
39153                 y : y
39154             });
39155             
39156             return pos;
39157             
39158         }
39159         
39160         if(box[0].size == 'xs' && box[1].size == 'xs'){
39161             
39162             pos.push({
39163                 x : x,
39164                 y : y
39165             });
39166
39167             pos.push({
39168                 x : x,
39169                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39170             });
39171             
39172             pos.push({
39173                 x : x + (this.unitWidth + this.gutter) * 1,
39174                 y : y
39175             });
39176             
39177             return pos;
39178             
39179         }
39180         
39181         pos.push({
39182             x : x,
39183             y : y
39184         });
39185
39186         pos.push({
39187             x : x + (this.unitWidth + this.gutter) * 2,
39188             y : y
39189         });
39190
39191         pos.push({
39192             x : x + (this.unitWidth + this.gutter) * 2,
39193             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39194         });
39195             
39196         return pos;
39197         
39198     },
39199     
39200     getVerticalFourBoxColPositions : function(x, y, box)
39201     {
39202         var pos = [];
39203         
39204         if(box[0].size == 'xs'){
39205             
39206             pos.push({
39207                 x : x,
39208                 y : y
39209             });
39210
39211             pos.push({
39212                 x : x,
39213                 y : y + (this.unitHeight + this.gutter) * 1
39214             });
39215             
39216             pos.push({
39217                 x : x,
39218                 y : y + (this.unitHeight + this.gutter) * 2
39219             });
39220             
39221             pos.push({
39222                 x : x + (this.unitWidth + this.gutter) * 1,
39223                 y : y
39224             });
39225             
39226             return pos;
39227             
39228         }
39229         
39230         pos.push({
39231             x : x,
39232             y : y
39233         });
39234
39235         pos.push({
39236             x : x + (this.unitWidth + this.gutter) * 2,
39237             y : y
39238         });
39239
39240         pos.push({
39241             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39242             y : y + (this.unitHeight + this.gutter) * 1
39243         });
39244
39245         pos.push({
39246             x : x + (this.unitWidth + this.gutter) * 2,
39247             y : y + (this.unitWidth + this.gutter) * 2
39248         });
39249
39250         return pos;
39251         
39252     },
39253     
39254     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39255     {
39256         var pos = [];
39257         
39258         if(box[0].size == 'md-left'){
39259             pos.push({
39260                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39261                 y : minY
39262             });
39263             
39264             return pos;
39265         }
39266         
39267         if(box[0].size == 'md-right'){
39268             pos.push({
39269                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39270                 y : minY + (this.unitWidth + this.gutter) * 1
39271             });
39272             
39273             return pos;
39274         }
39275         
39276         var rand = Math.floor(Math.random() * (4 - box[0].y));
39277         
39278         pos.push({
39279             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39280             y : minY + (this.unitWidth + this.gutter) * rand
39281         });
39282         
39283         return pos;
39284         
39285     },
39286     
39287     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39288     {
39289         var pos = [];
39290         
39291         if(box[0].size == 'xs'){
39292             
39293             pos.push({
39294                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39295                 y : minY
39296             });
39297
39298             pos.push({
39299                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39300                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39301             });
39302             
39303             return pos;
39304             
39305         }
39306         
39307         pos.push({
39308             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39309             y : minY
39310         });
39311
39312         pos.push({
39313             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39314             y : minY + (this.unitWidth + this.gutter) * 2
39315         });
39316         
39317         return pos;
39318         
39319     },
39320     
39321     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39322     {
39323         var pos = [];
39324         
39325         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39326             
39327             pos.push({
39328                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39329                 y : minY
39330             });
39331
39332             pos.push({
39333                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39334                 y : minY + (this.unitWidth + this.gutter) * 1
39335             });
39336             
39337             pos.push({
39338                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39339                 y : minY + (this.unitWidth + this.gutter) * 2
39340             });
39341             
39342             return pos;
39343             
39344         }
39345         
39346         if(box[0].size == 'xs' && box[1].size == 'xs'){
39347             
39348             pos.push({
39349                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39350                 y : minY
39351             });
39352
39353             pos.push({
39354                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39355                 y : minY
39356             });
39357             
39358             pos.push({
39359                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39360                 y : minY + (this.unitWidth + this.gutter) * 1
39361             });
39362             
39363             return pos;
39364             
39365         }
39366         
39367         pos.push({
39368             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39369             y : minY
39370         });
39371
39372         pos.push({
39373             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39374             y : minY + (this.unitWidth + this.gutter) * 2
39375         });
39376
39377         pos.push({
39378             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39379             y : minY + (this.unitWidth + this.gutter) * 2
39380         });
39381             
39382         return pos;
39383         
39384     },
39385     
39386     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39387     {
39388         var pos = [];
39389         
39390         if(box[0].size == 'xs'){
39391             
39392             pos.push({
39393                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39394                 y : minY
39395             });
39396
39397             pos.push({
39398                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39399                 y : minY
39400             });
39401             
39402             pos.push({
39403                 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),
39404                 y : minY
39405             });
39406             
39407             pos.push({
39408                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39409                 y : minY + (this.unitWidth + this.gutter) * 1
39410             });
39411             
39412             return pos;
39413             
39414         }
39415         
39416         pos.push({
39417             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39418             y : minY
39419         });
39420         
39421         pos.push({
39422             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39423             y : minY + (this.unitWidth + this.gutter) * 2
39424         });
39425         
39426         pos.push({
39427             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39428             y : minY + (this.unitWidth + this.gutter) * 2
39429         });
39430         
39431         pos.push({
39432             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),
39433             y : minY + (this.unitWidth + this.gutter) * 2
39434         });
39435
39436         return pos;
39437         
39438     },
39439     
39440     /**
39441     * remove a Masonry Brick
39442     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39443     */
39444     removeBrick : function(brick_id)
39445     {
39446         if (!brick_id) {
39447             return;
39448         }
39449         
39450         for (var i = 0; i<this.bricks.length; i++) {
39451             if (this.bricks[i].id == brick_id) {
39452                 this.bricks.splice(i,1);
39453                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39454                 this.initial();
39455             }
39456         }
39457     },
39458     
39459     /**
39460     * adds a Masonry Brick
39461     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39462     */
39463     addBrick : function(cfg)
39464     {
39465         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39466         //this.register(cn);
39467         cn.parentId = this.id;
39468         cn.render(this.el);
39469         return cn;
39470     },
39471     
39472     /**
39473     * register a Masonry Brick
39474     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39475     */
39476     
39477     register : function(brick)
39478     {
39479         this.bricks.push(brick);
39480         brick.masonryId = this.id;
39481     },
39482     
39483     /**
39484     * clear all the Masonry Brick
39485     */
39486     clearAll : function()
39487     {
39488         this.bricks = [];
39489         //this.getChildContainer().dom.innerHTML = "";
39490         this.el.dom.innerHTML = '';
39491     },
39492     
39493     getSelected : function()
39494     {
39495         if (!this.selectedBrick) {
39496             return false;
39497         }
39498         
39499         return this.selectedBrick;
39500     }
39501 });
39502
39503 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39504     
39505     groups: {},
39506      /**
39507     * register a Masonry Layout
39508     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39509     */
39510     
39511     register : function(layout)
39512     {
39513         this.groups[layout.id] = layout;
39514     },
39515     /**
39516     * fetch a  Masonry Layout based on the masonry layout ID
39517     * @param {string} the masonry layout to add
39518     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39519     */
39520     
39521     get: function(layout_id) {
39522         if (typeof(this.groups[layout_id]) == 'undefined') {
39523             return false;
39524         }
39525         return this.groups[layout_id] ;
39526     }
39527     
39528     
39529     
39530 });
39531
39532  
39533
39534  /**
39535  *
39536  * This is based on 
39537  * http://masonry.desandro.com
39538  *
39539  * The idea is to render all the bricks based on vertical width...
39540  *
39541  * The original code extends 'outlayer' - we might need to use that....
39542  * 
39543  */
39544
39545
39546 /**
39547  * @class Roo.bootstrap.LayoutMasonryAuto
39548  * @extends Roo.bootstrap.Component
39549  * Bootstrap Layout Masonry class
39550  * 
39551  * @constructor
39552  * Create a new Element
39553  * @param {Object} config The config object
39554  */
39555
39556 Roo.bootstrap.LayoutMasonryAuto = function(config){
39557     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39558 };
39559
39560 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39561     
39562       /**
39563      * @cfg {Boolean} isFitWidth  - resize the width..
39564      */   
39565     isFitWidth : false,  // options..
39566     /**
39567      * @cfg {Boolean} isOriginLeft = left align?
39568      */   
39569     isOriginLeft : true,
39570     /**
39571      * @cfg {Boolean} isOriginTop = top align?
39572      */   
39573     isOriginTop : false,
39574     /**
39575      * @cfg {Boolean} isLayoutInstant = no animation?
39576      */   
39577     isLayoutInstant : false, // needed?
39578     /**
39579      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39580      */   
39581     isResizingContainer : true,
39582     /**
39583      * @cfg {Number} columnWidth  width of the columns 
39584      */   
39585     
39586     columnWidth : 0,
39587     
39588     /**
39589      * @cfg {Number} maxCols maximum number of columns
39590      */   
39591     
39592     maxCols: 0,
39593     /**
39594      * @cfg {Number} padHeight padding below box..
39595      */   
39596     
39597     padHeight : 10, 
39598     
39599     /**
39600      * @cfg {Boolean} isAutoInitial defalut true
39601      */   
39602     
39603     isAutoInitial : true, 
39604     
39605     // private?
39606     gutter : 0,
39607     
39608     containerWidth: 0,
39609     initialColumnWidth : 0,
39610     currentSize : null,
39611     
39612     colYs : null, // array.
39613     maxY : 0,
39614     padWidth: 10,
39615     
39616     
39617     tag: 'div',
39618     cls: '',
39619     bricks: null, //CompositeElement
39620     cols : 0, // array?
39621     // element : null, // wrapped now this.el
39622     _isLayoutInited : null, 
39623     
39624     
39625     getAutoCreate : function(){
39626         
39627         var cfg = {
39628             tag: this.tag,
39629             cls: 'blog-masonary-wrapper ' + this.cls,
39630             cn : {
39631                 cls : 'mas-boxes masonary'
39632             }
39633         };
39634         
39635         return cfg;
39636     },
39637     
39638     getChildContainer: function( )
39639     {
39640         if (this.boxesEl) {
39641             return this.boxesEl;
39642         }
39643         
39644         this.boxesEl = this.el.select('.mas-boxes').first();
39645         
39646         return this.boxesEl;
39647     },
39648     
39649     
39650     initEvents : function()
39651     {
39652         var _this = this;
39653         
39654         if(this.isAutoInitial){
39655             Roo.log('hook children rendered');
39656             this.on('childrenrendered', function() {
39657                 Roo.log('children rendered');
39658                 _this.initial();
39659             } ,this);
39660         }
39661         
39662     },
39663     
39664     initial : function()
39665     {
39666         this.reloadItems();
39667
39668         this.currentSize = this.el.getBox(true);
39669
39670         /// was window resize... - let's see if this works..
39671         Roo.EventManager.onWindowResize(this.resize, this); 
39672
39673         if(!this.isAutoInitial){
39674             this.layout();
39675             return;
39676         }
39677         
39678         this.layout.defer(500,this);
39679     },
39680     
39681     reloadItems: function()
39682     {
39683         this.bricks = this.el.select('.masonry-brick', true);
39684         
39685         this.bricks.each(function(b) {
39686             //Roo.log(b.getSize());
39687             if (!b.attr('originalwidth')) {
39688                 b.attr('originalwidth',  b.getSize().width);
39689             }
39690             
39691         });
39692         
39693         Roo.log(this.bricks.elements.length);
39694     },
39695     
39696     resize : function()
39697     {
39698         Roo.log('resize');
39699         var cs = this.el.getBox(true);
39700         
39701         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39702             Roo.log("no change in with or X");
39703             return;
39704         }
39705         this.currentSize = cs;
39706         this.layout();
39707     },
39708     
39709     layout : function()
39710     {
39711          Roo.log('layout');
39712         this._resetLayout();
39713         //this._manageStamps();
39714       
39715         // don't animate first layout
39716         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39717         this.layoutItems( isInstant );
39718       
39719         // flag for initalized
39720         this._isLayoutInited = true;
39721     },
39722     
39723     layoutItems : function( isInstant )
39724     {
39725         //var items = this._getItemsForLayout( this.items );
39726         // original code supports filtering layout items.. we just ignore it..
39727         
39728         this._layoutItems( this.bricks , isInstant );
39729       
39730         this._postLayout();
39731     },
39732     _layoutItems : function ( items , isInstant)
39733     {
39734        //this.fireEvent( 'layout', this, items );
39735     
39736
39737         if ( !items || !items.elements.length ) {
39738           // no items, emit event with empty array
39739             return;
39740         }
39741
39742         var queue = [];
39743         items.each(function(item) {
39744             Roo.log("layout item");
39745             Roo.log(item);
39746             // get x/y object from method
39747             var position = this._getItemLayoutPosition( item );
39748             // enqueue
39749             position.item = item;
39750             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39751             queue.push( position );
39752         }, this);
39753       
39754         this._processLayoutQueue( queue );
39755     },
39756     /** Sets position of item in DOM
39757     * @param {Element} item
39758     * @param {Number} x - horizontal position
39759     * @param {Number} y - vertical position
39760     * @param {Boolean} isInstant - disables transitions
39761     */
39762     _processLayoutQueue : function( queue )
39763     {
39764         for ( var i=0, len = queue.length; i < len; i++ ) {
39765             var obj = queue[i];
39766             obj.item.position('absolute');
39767             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39768         }
39769     },
39770       
39771     
39772     /**
39773     * Any logic you want to do after each layout,
39774     * i.e. size the container
39775     */
39776     _postLayout : function()
39777     {
39778         this.resizeContainer();
39779     },
39780     
39781     resizeContainer : function()
39782     {
39783         if ( !this.isResizingContainer ) {
39784             return;
39785         }
39786         var size = this._getContainerSize();
39787         if ( size ) {
39788             this.el.setSize(size.width,size.height);
39789             this.boxesEl.setSize(size.width,size.height);
39790         }
39791     },
39792     
39793     
39794     
39795     _resetLayout : function()
39796     {
39797         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39798         this.colWidth = this.el.getWidth();
39799         //this.gutter = this.el.getWidth(); 
39800         
39801         this.measureColumns();
39802
39803         // reset column Y
39804         var i = this.cols;
39805         this.colYs = [];
39806         while (i--) {
39807             this.colYs.push( 0 );
39808         }
39809     
39810         this.maxY = 0;
39811     },
39812
39813     measureColumns : function()
39814     {
39815         this.getContainerWidth();
39816       // if columnWidth is 0, default to outerWidth of first item
39817         if ( !this.columnWidth ) {
39818             var firstItem = this.bricks.first();
39819             Roo.log(firstItem);
39820             this.columnWidth  = this.containerWidth;
39821             if (firstItem && firstItem.attr('originalwidth') ) {
39822                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39823             }
39824             // columnWidth fall back to item of first element
39825             Roo.log("set column width?");
39826                         this.initialColumnWidth = this.columnWidth  ;
39827
39828             // if first elem has no width, default to size of container
39829             
39830         }
39831         
39832         
39833         if (this.initialColumnWidth) {
39834             this.columnWidth = this.initialColumnWidth;
39835         }
39836         
39837         
39838             
39839         // column width is fixed at the top - however if container width get's smaller we should
39840         // reduce it...
39841         
39842         // this bit calcs how man columns..
39843             
39844         var columnWidth = this.columnWidth += this.gutter;
39845       
39846         // calculate columns
39847         var containerWidth = this.containerWidth + this.gutter;
39848         
39849         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39850         // fix rounding errors, typically with gutters
39851         var excess = columnWidth - containerWidth % columnWidth;
39852         
39853         
39854         // if overshoot is less than a pixel, round up, otherwise floor it
39855         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39856         cols = Math[ mathMethod ]( cols );
39857         this.cols = Math.max( cols, 1 );
39858         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39859         
39860          // padding positioning..
39861         var totalColWidth = this.cols * this.columnWidth;
39862         var padavail = this.containerWidth - totalColWidth;
39863         // so for 2 columns - we need 3 'pads'
39864         
39865         var padNeeded = (1+this.cols) * this.padWidth;
39866         
39867         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39868         
39869         this.columnWidth += padExtra
39870         //this.padWidth = Math.floor(padavail /  ( this.cols));
39871         
39872         // adjust colum width so that padding is fixed??
39873         
39874         // we have 3 columns ... total = width * 3
39875         // we have X left over... that should be used by 
39876         
39877         //if (this.expandC) {
39878             
39879         //}
39880         
39881         
39882         
39883     },
39884     
39885     getContainerWidth : function()
39886     {
39887        /* // container is parent if fit width
39888         var container = this.isFitWidth ? this.element.parentNode : this.element;
39889         // check that this.size and size are there
39890         // IE8 triggers resize on body size change, so they might not be
39891         
39892         var size = getSize( container );  //FIXME
39893         this.containerWidth = size && size.innerWidth; //FIXME
39894         */
39895          
39896         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39897         
39898     },
39899     
39900     _getItemLayoutPosition : function( item )  // what is item?
39901     {
39902         // we resize the item to our columnWidth..
39903       
39904         item.setWidth(this.columnWidth);
39905         item.autoBoxAdjust  = false;
39906         
39907         var sz = item.getSize();
39908  
39909         // how many columns does this brick span
39910         var remainder = this.containerWidth % this.columnWidth;
39911         
39912         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39913         // round if off by 1 pixel, otherwise use ceil
39914         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39915         colSpan = Math.min( colSpan, this.cols );
39916         
39917         // normally this should be '1' as we dont' currently allow multi width columns..
39918         
39919         var colGroup = this._getColGroup( colSpan );
39920         // get the minimum Y value from the columns
39921         var minimumY = Math.min.apply( Math, colGroup );
39922         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39923         
39924         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39925          
39926         // position the brick
39927         var position = {
39928             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39929             y: this.currentSize.y + minimumY + this.padHeight
39930         };
39931         
39932         Roo.log(position);
39933         // apply setHeight to necessary columns
39934         var setHeight = minimumY + sz.height + this.padHeight;
39935         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39936         
39937         var setSpan = this.cols + 1 - colGroup.length;
39938         for ( var i = 0; i < setSpan; i++ ) {
39939           this.colYs[ shortColIndex + i ] = setHeight ;
39940         }
39941       
39942         return position;
39943     },
39944     
39945     /**
39946      * @param {Number} colSpan - number of columns the element spans
39947      * @returns {Array} colGroup
39948      */
39949     _getColGroup : function( colSpan )
39950     {
39951         if ( colSpan < 2 ) {
39952           // if brick spans only one column, use all the column Ys
39953           return this.colYs;
39954         }
39955       
39956         var colGroup = [];
39957         // how many different places could this brick fit horizontally
39958         var groupCount = this.cols + 1 - colSpan;
39959         // for each group potential horizontal position
39960         for ( var i = 0; i < groupCount; i++ ) {
39961           // make an array of colY values for that one group
39962           var groupColYs = this.colYs.slice( i, i + colSpan );
39963           // and get the max value of the array
39964           colGroup[i] = Math.max.apply( Math, groupColYs );
39965         }
39966         return colGroup;
39967     },
39968     /*
39969     _manageStamp : function( stamp )
39970     {
39971         var stampSize =  stamp.getSize();
39972         var offset = stamp.getBox();
39973         // get the columns that this stamp affects
39974         var firstX = this.isOriginLeft ? offset.x : offset.right;
39975         var lastX = firstX + stampSize.width;
39976         var firstCol = Math.floor( firstX / this.columnWidth );
39977         firstCol = Math.max( 0, firstCol );
39978         
39979         var lastCol = Math.floor( lastX / this.columnWidth );
39980         // lastCol should not go over if multiple of columnWidth #425
39981         lastCol -= lastX % this.columnWidth ? 0 : 1;
39982         lastCol = Math.min( this.cols - 1, lastCol );
39983         
39984         // set colYs to bottom of the stamp
39985         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39986             stampSize.height;
39987             
39988         for ( var i = firstCol; i <= lastCol; i++ ) {
39989           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39990         }
39991     },
39992     */
39993     
39994     _getContainerSize : function()
39995     {
39996         this.maxY = Math.max.apply( Math, this.colYs );
39997         var size = {
39998             height: this.maxY
39999         };
40000       
40001         if ( this.isFitWidth ) {
40002             size.width = this._getContainerFitWidth();
40003         }
40004       
40005         return size;
40006     },
40007     
40008     _getContainerFitWidth : function()
40009     {
40010         var unusedCols = 0;
40011         // count unused columns
40012         var i = this.cols;
40013         while ( --i ) {
40014           if ( this.colYs[i] !== 0 ) {
40015             break;
40016           }
40017           unusedCols++;
40018         }
40019         // fit container to columns that have been used
40020         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40021     },
40022     
40023     needsResizeLayout : function()
40024     {
40025         var previousWidth = this.containerWidth;
40026         this.getContainerWidth();
40027         return previousWidth !== this.containerWidth;
40028     }
40029  
40030 });
40031
40032  
40033
40034  /*
40035  * - LGPL
40036  *
40037  * element
40038  * 
40039  */
40040
40041 /**
40042  * @class Roo.bootstrap.MasonryBrick
40043  * @extends Roo.bootstrap.Component
40044  * Bootstrap MasonryBrick class
40045  * 
40046  * @constructor
40047  * Create a new MasonryBrick
40048  * @param {Object} config The config object
40049  */
40050
40051 Roo.bootstrap.MasonryBrick = function(config){
40052     
40053     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40054     
40055     Roo.bootstrap.MasonryBrick.register(this);
40056     
40057     this.addEvents({
40058         // raw events
40059         /**
40060          * @event click
40061          * When a MasonryBrick is clcik
40062          * @param {Roo.bootstrap.MasonryBrick} this
40063          * @param {Roo.EventObject} e
40064          */
40065         "click" : true
40066     });
40067 };
40068
40069 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40070     
40071     /**
40072      * @cfg {String} title
40073      */   
40074     title : '',
40075     /**
40076      * @cfg {String} html
40077      */   
40078     html : '',
40079     /**
40080      * @cfg {String} bgimage
40081      */   
40082     bgimage : '',
40083     /**
40084      * @cfg {String} videourl
40085      */   
40086     videourl : '',
40087     /**
40088      * @cfg {String} cls
40089      */   
40090     cls : '',
40091     /**
40092      * @cfg {String} href
40093      */   
40094     href : '',
40095     /**
40096      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40097      */   
40098     size : 'xs',
40099     
40100     /**
40101      * @cfg {String} placetitle (center|bottom)
40102      */   
40103     placetitle : '',
40104     
40105     /**
40106      * @cfg {Boolean} isFitContainer defalut true
40107      */   
40108     isFitContainer : true, 
40109     
40110     /**
40111      * @cfg {Boolean} preventDefault defalut false
40112      */   
40113     preventDefault : false, 
40114     
40115     /**
40116      * @cfg {Boolean} inverse defalut false
40117      */   
40118     maskInverse : false, 
40119     
40120     getAutoCreate : function()
40121     {
40122         if(!this.isFitContainer){
40123             return this.getSplitAutoCreate();
40124         }
40125         
40126         var cls = 'masonry-brick masonry-brick-full';
40127         
40128         if(this.href.length){
40129             cls += ' masonry-brick-link';
40130         }
40131         
40132         if(this.bgimage.length){
40133             cls += ' masonry-brick-image';
40134         }
40135         
40136         if(this.maskInverse){
40137             cls += ' mask-inverse';
40138         }
40139         
40140         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40141             cls += ' enable-mask';
40142         }
40143         
40144         if(this.size){
40145             cls += ' masonry-' + this.size + '-brick';
40146         }
40147         
40148         if(this.placetitle.length){
40149             
40150             switch (this.placetitle) {
40151                 case 'center' :
40152                     cls += ' masonry-center-title';
40153                     break;
40154                 case 'bottom' :
40155                     cls += ' masonry-bottom-title';
40156                     break;
40157                 default:
40158                     break;
40159             }
40160             
40161         } else {
40162             if(!this.html.length && !this.bgimage.length){
40163                 cls += ' masonry-center-title';
40164             }
40165
40166             if(!this.html.length && this.bgimage.length){
40167                 cls += ' masonry-bottom-title';
40168             }
40169         }
40170         
40171         if(this.cls){
40172             cls += ' ' + this.cls;
40173         }
40174         
40175         var cfg = {
40176             tag: (this.href.length) ? 'a' : 'div',
40177             cls: cls,
40178             cn: [
40179                 {
40180                     tag: 'div',
40181                     cls: 'masonry-brick-mask'
40182                 },
40183                 {
40184                     tag: 'div',
40185                     cls: 'masonry-brick-paragraph',
40186                     cn: []
40187                 }
40188             ]
40189         };
40190         
40191         if(this.href.length){
40192             cfg.href = this.href;
40193         }
40194         
40195         var cn = cfg.cn[1].cn;
40196         
40197         if(this.title.length){
40198             cn.push({
40199                 tag: 'h4',
40200                 cls: 'masonry-brick-title',
40201                 html: this.title
40202             });
40203         }
40204         
40205         if(this.html.length){
40206             cn.push({
40207                 tag: 'p',
40208                 cls: 'masonry-brick-text',
40209                 html: this.html
40210             });
40211         }
40212         
40213         if (!this.title.length && !this.html.length) {
40214             cfg.cn[1].cls += ' hide';
40215         }
40216         
40217         if(this.bgimage.length){
40218             cfg.cn.push({
40219                 tag: 'img',
40220                 cls: 'masonry-brick-image-view',
40221                 src: this.bgimage
40222             });
40223         }
40224         
40225         if(this.videourl.length){
40226             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40227             // youtube support only?
40228             cfg.cn.push({
40229                 tag: 'iframe',
40230                 cls: 'masonry-brick-image-view',
40231                 src: vurl,
40232                 frameborder : 0,
40233                 allowfullscreen : true
40234             });
40235         }
40236         
40237         return cfg;
40238         
40239     },
40240     
40241     getSplitAutoCreate : function()
40242     {
40243         var cls = 'masonry-brick masonry-brick-split';
40244         
40245         if(this.href.length){
40246             cls += ' masonry-brick-link';
40247         }
40248         
40249         if(this.bgimage.length){
40250             cls += ' masonry-brick-image';
40251         }
40252         
40253         if(this.size){
40254             cls += ' masonry-' + this.size + '-brick';
40255         }
40256         
40257         switch (this.placetitle) {
40258             case 'center' :
40259                 cls += ' masonry-center-title';
40260                 break;
40261             case 'bottom' :
40262                 cls += ' masonry-bottom-title';
40263                 break;
40264             default:
40265                 if(!this.bgimage.length){
40266                     cls += ' masonry-center-title';
40267                 }
40268
40269                 if(this.bgimage.length){
40270                     cls += ' masonry-bottom-title';
40271                 }
40272                 break;
40273         }
40274         
40275         if(this.cls){
40276             cls += ' ' + this.cls;
40277         }
40278         
40279         var cfg = {
40280             tag: (this.href.length) ? 'a' : 'div',
40281             cls: cls,
40282             cn: [
40283                 {
40284                     tag: 'div',
40285                     cls: 'masonry-brick-split-head',
40286                     cn: [
40287                         {
40288                             tag: 'div',
40289                             cls: 'masonry-brick-paragraph',
40290                             cn: []
40291                         }
40292                     ]
40293                 },
40294                 {
40295                     tag: 'div',
40296                     cls: 'masonry-brick-split-body',
40297                     cn: []
40298                 }
40299             ]
40300         };
40301         
40302         if(this.href.length){
40303             cfg.href = this.href;
40304         }
40305         
40306         if(this.title.length){
40307             cfg.cn[0].cn[0].cn.push({
40308                 tag: 'h4',
40309                 cls: 'masonry-brick-title',
40310                 html: this.title
40311             });
40312         }
40313         
40314         if(this.html.length){
40315             cfg.cn[1].cn.push({
40316                 tag: 'p',
40317                 cls: 'masonry-brick-text',
40318                 html: this.html
40319             });
40320         }
40321
40322         if(this.bgimage.length){
40323             cfg.cn[0].cn.push({
40324                 tag: 'img',
40325                 cls: 'masonry-brick-image-view',
40326                 src: this.bgimage
40327             });
40328         }
40329         
40330         if(this.videourl.length){
40331             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40332             // youtube support only?
40333             cfg.cn[0].cn.cn.push({
40334                 tag: 'iframe',
40335                 cls: 'masonry-brick-image-view',
40336                 src: vurl,
40337                 frameborder : 0,
40338                 allowfullscreen : true
40339             });
40340         }
40341         
40342         return cfg;
40343     },
40344     
40345     initEvents: function() 
40346     {
40347         switch (this.size) {
40348             case 'xs' :
40349                 this.x = 1;
40350                 this.y = 1;
40351                 break;
40352             case 'sm' :
40353                 this.x = 2;
40354                 this.y = 2;
40355                 break;
40356             case 'md' :
40357             case 'md-left' :
40358             case 'md-right' :
40359                 this.x = 3;
40360                 this.y = 3;
40361                 break;
40362             case 'tall' :
40363                 this.x = 2;
40364                 this.y = 3;
40365                 break;
40366             case 'wide' :
40367                 this.x = 3;
40368                 this.y = 2;
40369                 break;
40370             case 'wide-thin' :
40371                 this.x = 3;
40372                 this.y = 1;
40373                 break;
40374                         
40375             default :
40376                 break;
40377         }
40378         
40379         if(Roo.isTouch){
40380             this.el.on('touchstart', this.onTouchStart, this);
40381             this.el.on('touchmove', this.onTouchMove, this);
40382             this.el.on('touchend', this.onTouchEnd, this);
40383             this.el.on('contextmenu', this.onContextMenu, this);
40384         } else {
40385             this.el.on('mouseenter'  ,this.enter, this);
40386             this.el.on('mouseleave', this.leave, this);
40387             this.el.on('click', this.onClick, this);
40388         }
40389         
40390         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40391             this.parent().bricks.push(this);   
40392         }
40393         
40394     },
40395     
40396     onClick: function(e, el)
40397     {
40398         var time = this.endTimer - this.startTimer;
40399         // Roo.log(e.preventDefault());
40400         if(Roo.isTouch){
40401             if(time > 1000){
40402                 e.preventDefault();
40403                 return;
40404             }
40405         }
40406         
40407         if(!this.preventDefault){
40408             return;
40409         }
40410         
40411         e.preventDefault();
40412         
40413         if (this.activeClass != '') {
40414             this.selectBrick();
40415         }
40416         
40417         this.fireEvent('click', this, e);
40418     },
40419     
40420     enter: function(e, el)
40421     {
40422         e.preventDefault();
40423         
40424         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40425             return;
40426         }
40427         
40428         if(this.bgimage.length && this.html.length){
40429             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40430         }
40431     },
40432     
40433     leave: function(e, el)
40434     {
40435         e.preventDefault();
40436         
40437         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40438             return;
40439         }
40440         
40441         if(this.bgimage.length && this.html.length){
40442             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40443         }
40444     },
40445     
40446     onTouchStart: function(e, el)
40447     {
40448 //        e.preventDefault();
40449         
40450         this.touchmoved = false;
40451         
40452         if(!this.isFitContainer){
40453             return;
40454         }
40455         
40456         if(!this.bgimage.length || !this.html.length){
40457             return;
40458         }
40459         
40460         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40461         
40462         this.timer = new Date().getTime();
40463         
40464     },
40465     
40466     onTouchMove: function(e, el)
40467     {
40468         this.touchmoved = true;
40469     },
40470     
40471     onContextMenu : function(e,el)
40472     {
40473         e.preventDefault();
40474         e.stopPropagation();
40475         return false;
40476     },
40477     
40478     onTouchEnd: function(e, el)
40479     {
40480 //        e.preventDefault();
40481         
40482         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40483         
40484             this.leave(e,el);
40485             
40486             return;
40487         }
40488         
40489         if(!this.bgimage.length || !this.html.length){
40490             
40491             if(this.href.length){
40492                 window.location.href = this.href;
40493             }
40494             
40495             return;
40496         }
40497         
40498         if(!this.isFitContainer){
40499             return;
40500         }
40501         
40502         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40503         
40504         window.location.href = this.href;
40505     },
40506     
40507     //selection on single brick only
40508     selectBrick : function() {
40509         
40510         if (!this.parentId) {
40511             return;
40512         }
40513         
40514         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40515         var index = m.selectedBrick.indexOf(this.id);
40516         
40517         if ( index > -1) {
40518             m.selectedBrick.splice(index,1);
40519             this.el.removeClass(this.activeClass);
40520             return;
40521         }
40522         
40523         for(var i = 0; i < m.selectedBrick.length; i++) {
40524             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40525             b.el.removeClass(b.activeClass);
40526         }
40527         
40528         m.selectedBrick = [];
40529         
40530         m.selectedBrick.push(this.id);
40531         this.el.addClass(this.activeClass);
40532         return;
40533     },
40534     
40535     isSelected : function(){
40536         return this.el.hasClass(this.activeClass);
40537         
40538     }
40539 });
40540
40541 Roo.apply(Roo.bootstrap.MasonryBrick, {
40542     
40543     //groups: {},
40544     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40545      /**
40546     * register a Masonry Brick
40547     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40548     */
40549     
40550     register : function(brick)
40551     {
40552         //this.groups[brick.id] = brick;
40553         this.groups.add(brick.id, brick);
40554     },
40555     /**
40556     * fetch a  masonry brick based on the masonry brick ID
40557     * @param {string} the masonry brick to add
40558     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40559     */
40560     
40561     get: function(brick_id) 
40562     {
40563         // if (typeof(this.groups[brick_id]) == 'undefined') {
40564         //     return false;
40565         // }
40566         // return this.groups[brick_id] ;
40567         
40568         if(this.groups.key(brick_id)) {
40569             return this.groups.key(brick_id);
40570         }
40571         
40572         return false;
40573     }
40574     
40575     
40576     
40577 });
40578
40579  /*
40580  * - LGPL
40581  *
40582  * element
40583  * 
40584  */
40585
40586 /**
40587  * @class Roo.bootstrap.Brick
40588  * @extends Roo.bootstrap.Component
40589  * Bootstrap Brick class
40590  * 
40591  * @constructor
40592  * Create a new Brick
40593  * @param {Object} config The config object
40594  */
40595
40596 Roo.bootstrap.Brick = function(config){
40597     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40598     
40599     this.addEvents({
40600         // raw events
40601         /**
40602          * @event click
40603          * When a Brick is click
40604          * @param {Roo.bootstrap.Brick} this
40605          * @param {Roo.EventObject} e
40606          */
40607         "click" : true
40608     });
40609 };
40610
40611 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40612     
40613     /**
40614      * @cfg {String} title
40615      */   
40616     title : '',
40617     /**
40618      * @cfg {String} html
40619      */   
40620     html : '',
40621     /**
40622      * @cfg {String} bgimage
40623      */   
40624     bgimage : '',
40625     /**
40626      * @cfg {String} cls
40627      */   
40628     cls : '',
40629     /**
40630      * @cfg {String} href
40631      */   
40632     href : '',
40633     /**
40634      * @cfg {String} video
40635      */   
40636     video : '',
40637     /**
40638      * @cfg {Boolean} square
40639      */   
40640     square : true,
40641     
40642     getAutoCreate : function()
40643     {
40644         var cls = 'roo-brick';
40645         
40646         if(this.href.length){
40647             cls += ' roo-brick-link';
40648         }
40649         
40650         if(this.bgimage.length){
40651             cls += ' roo-brick-image';
40652         }
40653         
40654         if(!this.html.length && !this.bgimage.length){
40655             cls += ' roo-brick-center-title';
40656         }
40657         
40658         if(!this.html.length && this.bgimage.length){
40659             cls += ' roo-brick-bottom-title';
40660         }
40661         
40662         if(this.cls){
40663             cls += ' ' + this.cls;
40664         }
40665         
40666         var cfg = {
40667             tag: (this.href.length) ? 'a' : 'div',
40668             cls: cls,
40669             cn: [
40670                 {
40671                     tag: 'div',
40672                     cls: 'roo-brick-paragraph',
40673                     cn: []
40674                 }
40675             ]
40676         };
40677         
40678         if(this.href.length){
40679             cfg.href = this.href;
40680         }
40681         
40682         var cn = cfg.cn[0].cn;
40683         
40684         if(this.title.length){
40685             cn.push({
40686                 tag: 'h4',
40687                 cls: 'roo-brick-title',
40688                 html: this.title
40689             });
40690         }
40691         
40692         if(this.html.length){
40693             cn.push({
40694                 tag: 'p',
40695                 cls: 'roo-brick-text',
40696                 html: this.html
40697             });
40698         } else {
40699             cn.cls += ' hide';
40700         }
40701         
40702         if(this.bgimage.length){
40703             cfg.cn.push({
40704                 tag: 'img',
40705                 cls: 'roo-brick-image-view',
40706                 src: this.bgimage
40707             });
40708         }
40709         
40710         return cfg;
40711     },
40712     
40713     initEvents: function() 
40714     {
40715         if(this.title.length || this.html.length){
40716             this.el.on('mouseenter'  ,this.enter, this);
40717             this.el.on('mouseleave', this.leave, this);
40718         }
40719         
40720         Roo.EventManager.onWindowResize(this.resize, this); 
40721         
40722         if(this.bgimage.length){
40723             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40724             this.imageEl.on('load', this.onImageLoad, this);
40725             return;
40726         }
40727         
40728         this.resize();
40729     },
40730     
40731     onImageLoad : function()
40732     {
40733         this.resize();
40734     },
40735     
40736     resize : function()
40737     {
40738         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40739         
40740         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40741         
40742         if(this.bgimage.length){
40743             var image = this.el.select('.roo-brick-image-view', true).first();
40744             
40745             image.setWidth(paragraph.getWidth());
40746             
40747             if(this.square){
40748                 image.setHeight(paragraph.getWidth());
40749             }
40750             
40751             this.el.setHeight(image.getHeight());
40752             paragraph.setHeight(image.getHeight());
40753             
40754         }
40755         
40756     },
40757     
40758     enter: function(e, el)
40759     {
40760         e.preventDefault();
40761         
40762         if(this.bgimage.length){
40763             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40764             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40765         }
40766     },
40767     
40768     leave: function(e, el)
40769     {
40770         e.preventDefault();
40771         
40772         if(this.bgimage.length){
40773             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40774             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40775         }
40776     }
40777     
40778 });
40779
40780  
40781
40782  /*
40783  * - LGPL
40784  *
40785  * Number field 
40786  */
40787
40788 /**
40789  * @class Roo.bootstrap.form.NumberField
40790  * @extends Roo.bootstrap.form.Input
40791  * Bootstrap NumberField class
40792  * 
40793  * 
40794  * 
40795  * 
40796  * @constructor
40797  * Create a new NumberField
40798  * @param {Object} config The config object
40799  */
40800
40801 Roo.bootstrap.form.NumberField = function(config){
40802     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40803 };
40804
40805 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40806     
40807     /**
40808      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40809      */
40810     allowDecimals : true,
40811     /**
40812      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40813      */
40814     decimalSeparator : ".",
40815     /**
40816      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40817      */
40818     decimalPrecision : 2,
40819     /**
40820      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40821      */
40822     allowNegative : true,
40823     
40824     /**
40825      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40826      */
40827     allowZero: true,
40828     /**
40829      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40830      */
40831     minValue : Number.NEGATIVE_INFINITY,
40832     /**
40833      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40834      */
40835     maxValue : Number.MAX_VALUE,
40836     /**
40837      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40838      */
40839     minText : "The minimum value for this field is {0}",
40840     /**
40841      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40842      */
40843     maxText : "The maximum value for this field is {0}",
40844     /**
40845      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40846      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40847      */
40848     nanText : "{0} is not a valid number",
40849     /**
40850      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40851      */
40852     thousandsDelimiter : false,
40853     /**
40854      * @cfg {String} valueAlign alignment of value
40855      */
40856     valueAlign : "left",
40857
40858     getAutoCreate : function()
40859     {
40860         var hiddenInput = {
40861             tag: 'input',
40862             type: 'hidden',
40863             id: Roo.id(),
40864             cls: 'hidden-number-input'
40865         };
40866         
40867         if (this.name) {
40868             hiddenInput.name = this.name;
40869         }
40870         
40871         this.name = '';
40872         
40873         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40874         
40875         this.name = hiddenInput.name;
40876         
40877         if(cfg.cn.length > 0) {
40878             cfg.cn.push(hiddenInput);
40879         }
40880         
40881         return cfg;
40882     },
40883
40884     // private
40885     initEvents : function()
40886     {   
40887         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40888         
40889         var allowed = "0123456789";
40890         
40891         if(this.allowDecimals){
40892             allowed += this.decimalSeparator;
40893         }
40894         
40895         if(this.allowNegative){
40896             allowed += "-";
40897         }
40898         
40899         if(this.thousandsDelimiter) {
40900             allowed += ",";
40901         }
40902         
40903         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40904         
40905         var keyPress = function(e){
40906             
40907             var k = e.getKey();
40908             
40909             var c = e.getCharCode();
40910             
40911             if(
40912                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40913                     allowed.indexOf(String.fromCharCode(c)) === -1
40914             ){
40915                 e.stopEvent();
40916                 return;
40917             }
40918             
40919             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40920                 return;
40921             }
40922             
40923             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40924                 e.stopEvent();
40925             }
40926         };
40927         
40928         this.el.on("keypress", keyPress, this);
40929     },
40930     
40931     validateValue : function(value)
40932     {
40933         
40934         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40935             return false;
40936         }
40937         
40938         var num = this.parseValue(value);
40939         
40940         if(isNaN(num)){
40941             this.markInvalid(String.format(this.nanText, value));
40942             return false;
40943         }
40944         
40945         if(num < this.minValue){
40946             this.markInvalid(String.format(this.minText, this.minValue));
40947             return false;
40948         }
40949         
40950         if(num > this.maxValue){
40951             this.markInvalid(String.format(this.maxText, this.maxValue));
40952             return false;
40953         }
40954         
40955         return true;
40956     },
40957
40958     getValue : function()
40959     {
40960         var v = this.hiddenEl().getValue();
40961         
40962         return this.fixPrecision(this.parseValue(v));
40963     },
40964
40965     parseValue : function(value)
40966     {
40967         if(this.thousandsDelimiter) {
40968             value += "";
40969             r = new RegExp(",", "g");
40970             value = value.replace(r, "");
40971         }
40972         
40973         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40974         return isNaN(value) ? '' : value;
40975     },
40976
40977     fixPrecision : function(value)
40978     {
40979         if(this.thousandsDelimiter) {
40980             value += "";
40981             r = new RegExp(",", "g");
40982             value = value.replace(r, "");
40983         }
40984         
40985         var nan = isNaN(value);
40986         
40987         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40988             return nan ? '' : value;
40989         }
40990         return parseFloat(value).toFixed(this.decimalPrecision);
40991     },
40992
40993     setValue : function(v)
40994     {
40995         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40996         
40997         this.value = v;
40998         
40999         if(this.rendered){
41000             
41001             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41002             
41003             this.inputEl().dom.value = (v == '') ? '' :
41004                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41005             
41006             if(!this.allowZero && v === '0') {
41007                 this.hiddenEl().dom.value = '';
41008                 this.inputEl().dom.value = '';
41009             }
41010             
41011             this.validate();
41012         }
41013     },
41014
41015     decimalPrecisionFcn : function(v)
41016     {
41017         return Math.floor(v);
41018     },
41019
41020     beforeBlur : function()
41021     {
41022         var v = this.parseValue(this.getRawValue());
41023         
41024         if(v || v === 0 || v === ''){
41025             this.setValue(v);
41026         }
41027     },
41028     
41029     hiddenEl : function()
41030     {
41031         return this.el.select('input.hidden-number-input',true).first();
41032     }
41033     
41034 });
41035
41036  
41037
41038 /*
41039 * Licence: LGPL
41040 */
41041
41042 /**
41043  * @class Roo.bootstrap.DocumentSlider
41044  * @extends Roo.bootstrap.Component
41045  * Bootstrap DocumentSlider class
41046  * 
41047  * @constructor
41048  * Create a new DocumentViewer
41049  * @param {Object} config The config object
41050  */
41051
41052 Roo.bootstrap.DocumentSlider = function(config){
41053     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41054     
41055     this.files = [];
41056     
41057     this.addEvents({
41058         /**
41059          * @event initial
41060          * Fire after initEvent
41061          * @param {Roo.bootstrap.DocumentSlider} this
41062          */
41063         "initial" : true,
41064         /**
41065          * @event update
41066          * Fire after update
41067          * @param {Roo.bootstrap.DocumentSlider} this
41068          */
41069         "update" : true,
41070         /**
41071          * @event click
41072          * Fire after click
41073          * @param {Roo.bootstrap.DocumentSlider} this
41074          */
41075         "click" : true
41076     });
41077 };
41078
41079 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41080     
41081     files : false,
41082     
41083     indicator : 0,
41084     
41085     getAutoCreate : function()
41086     {
41087         var cfg = {
41088             tag : 'div',
41089             cls : 'roo-document-slider',
41090             cn : [
41091                 {
41092                     tag : 'div',
41093                     cls : 'roo-document-slider-header',
41094                     cn : [
41095                         {
41096                             tag : 'div',
41097                             cls : 'roo-document-slider-header-title'
41098                         }
41099                     ]
41100                 },
41101                 {
41102                     tag : 'div',
41103                     cls : 'roo-document-slider-body',
41104                     cn : [
41105                         {
41106                             tag : 'div',
41107                             cls : 'roo-document-slider-prev',
41108                             cn : [
41109                                 {
41110                                     tag : 'i',
41111                                     cls : 'fa fa-chevron-left'
41112                                 }
41113                             ]
41114                         },
41115                         {
41116                             tag : 'div',
41117                             cls : 'roo-document-slider-thumb',
41118                             cn : [
41119                                 {
41120                                     tag : 'img',
41121                                     cls : 'roo-document-slider-image'
41122                                 }
41123                             ]
41124                         },
41125                         {
41126                             tag : 'div',
41127                             cls : 'roo-document-slider-next',
41128                             cn : [
41129                                 {
41130                                     tag : 'i',
41131                                     cls : 'fa fa-chevron-right'
41132                                 }
41133                             ]
41134                         }
41135                     ]
41136                 }
41137             ]
41138         };
41139         
41140         return cfg;
41141     },
41142     
41143     initEvents : function()
41144     {
41145         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41146         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41147         
41148         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41149         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41150         
41151         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41152         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41153         
41154         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41155         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41156         
41157         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41158         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41159         
41160         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41161         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41162         
41163         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41164         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41165         
41166         this.thumbEl.on('click', this.onClick, this);
41167         
41168         this.prevIndicator.on('click', this.prev, this);
41169         
41170         this.nextIndicator.on('click', this.next, this);
41171         
41172     },
41173     
41174     initial : function()
41175     {
41176         if(this.files.length){
41177             this.indicator = 1;
41178             this.update()
41179         }
41180         
41181         this.fireEvent('initial', this);
41182     },
41183     
41184     update : function()
41185     {
41186         this.imageEl.attr('src', this.files[this.indicator - 1]);
41187         
41188         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41189         
41190         this.prevIndicator.show();
41191         
41192         if(this.indicator == 1){
41193             this.prevIndicator.hide();
41194         }
41195         
41196         this.nextIndicator.show();
41197         
41198         if(this.indicator == this.files.length){
41199             this.nextIndicator.hide();
41200         }
41201         
41202         this.thumbEl.scrollTo('top');
41203         
41204         this.fireEvent('update', this);
41205     },
41206     
41207     onClick : function(e)
41208     {
41209         e.preventDefault();
41210         
41211         this.fireEvent('click', this);
41212     },
41213     
41214     prev : function(e)
41215     {
41216         e.preventDefault();
41217         
41218         this.indicator = Math.max(1, this.indicator - 1);
41219         
41220         this.update();
41221     },
41222     
41223     next : function(e)
41224     {
41225         e.preventDefault();
41226         
41227         this.indicator = Math.min(this.files.length, this.indicator + 1);
41228         
41229         this.update();
41230     }
41231 });
41232 /*
41233  * - LGPL
41234  *
41235  * RadioSet
41236  *
41237  *
41238  */
41239
41240 /**
41241  * @class Roo.bootstrap.form.RadioSet
41242  * @extends Roo.bootstrap.form.Input
41243  * @children Roo.bootstrap.form.Radio
41244  * Bootstrap RadioSet class
41245  * @cfg {String} indicatorpos (left|right) default left
41246  * @cfg {Boolean} inline (true|false) inline the element (default true)
41247  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41248  * @constructor
41249  * Create a new RadioSet
41250  * @param {Object} config The config object
41251  */
41252
41253 Roo.bootstrap.form.RadioSet = function(config){
41254     
41255     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41256     
41257     this.radioes = [];
41258     
41259     Roo.bootstrap.form.RadioSet.register(this);
41260     
41261     this.addEvents({
41262         /**
41263         * @event check
41264         * Fires when the element is checked or unchecked.
41265         * @param {Roo.bootstrap.form.RadioSet} this This radio
41266         * @param {Roo.bootstrap.form.Radio} item The checked item
41267         */
41268        check : true,
41269        /**
41270         * @event click
41271         * Fires when the element is click.
41272         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41273         * @param {Roo.bootstrap.form.Radio} item The checked item
41274         * @param {Roo.EventObject} e The event object
41275         */
41276        click : true
41277     });
41278     
41279 };
41280
41281 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41282
41283     radioes : false,
41284     
41285     inline : true,
41286     
41287     weight : '',
41288     
41289     indicatorpos : 'left',
41290     
41291     getAutoCreate : function()
41292     {
41293         var label = {
41294             tag : 'label',
41295             cls : 'roo-radio-set-label',
41296             cn : [
41297                 {
41298                     tag : 'span',
41299                     html : this.fieldLabel
41300                 }
41301             ]
41302         };
41303         if (Roo.bootstrap.version == 3) {
41304             
41305             
41306             if(this.indicatorpos == 'left'){
41307                 label.cn.unshift({
41308                     tag : 'i',
41309                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41310                     tooltip : 'This field is required'
41311                 });
41312             } else {
41313                 label.cn.push({
41314                     tag : 'i',
41315                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41316                     tooltip : 'This field is required'
41317                 });
41318             }
41319         }
41320         var items = {
41321             tag : 'div',
41322             cls : 'roo-radio-set-items'
41323         };
41324         
41325         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41326         
41327         if (align === 'left' && this.fieldLabel.length) {
41328             
41329             items = {
41330                 cls : "roo-radio-set-right", 
41331                 cn: [
41332                     items
41333                 ]
41334             };
41335             
41336             if(this.labelWidth > 12){
41337                 label.style = "width: " + this.labelWidth + 'px';
41338             }
41339             
41340             if(this.labelWidth < 13 && this.labelmd == 0){
41341                 this.labelmd = this.labelWidth;
41342             }
41343             
41344             if(this.labellg > 0){
41345                 label.cls += ' col-lg-' + this.labellg;
41346                 items.cls += ' col-lg-' + (12 - this.labellg);
41347             }
41348             
41349             if(this.labelmd > 0){
41350                 label.cls += ' col-md-' + this.labelmd;
41351                 items.cls += ' col-md-' + (12 - this.labelmd);
41352             }
41353             
41354             if(this.labelsm > 0){
41355                 label.cls += ' col-sm-' + this.labelsm;
41356                 items.cls += ' col-sm-' + (12 - this.labelsm);
41357             }
41358             
41359             if(this.labelxs > 0){
41360                 label.cls += ' col-xs-' + this.labelxs;
41361                 items.cls += ' col-xs-' + (12 - this.labelxs);
41362             }
41363         }
41364         
41365         var cfg = {
41366             tag : 'div',
41367             cls : 'roo-radio-set',
41368             cn : [
41369                 {
41370                     tag : 'input',
41371                     cls : 'roo-radio-set-input',
41372                     type : 'hidden',
41373                     name : this.name,
41374                     value : this.value ? this.value :  ''
41375                 },
41376                 label,
41377                 items
41378             ]
41379         };
41380         
41381         if(this.weight.length){
41382             cfg.cls += ' roo-radio-' + this.weight;
41383         }
41384         
41385         if(this.inline) {
41386             cfg.cls += ' roo-radio-set-inline';
41387         }
41388         
41389         var settings=this;
41390         ['xs','sm','md','lg'].map(function(size){
41391             if (settings[size]) {
41392                 cfg.cls += ' col-' + size + '-' + settings[size];
41393             }
41394         });
41395         
41396         return cfg;
41397         
41398     },
41399
41400     initEvents : function()
41401     {
41402         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41403         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41404         
41405         if(!this.fieldLabel.length){
41406             this.labelEl.hide();
41407         }
41408         
41409         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41410         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41411         
41412         this.indicator = this.indicatorEl();
41413         
41414         if(this.indicator){
41415             this.indicator.addClass('invisible');
41416         }
41417         
41418         this.originalValue = this.getValue();
41419         
41420     },
41421     
41422     inputEl: function ()
41423     {
41424         return this.el.select('.roo-radio-set-input', true).first();
41425     },
41426     
41427     getChildContainer : function()
41428     {
41429         return this.itemsEl;
41430     },
41431     
41432     register : function(item)
41433     {
41434         this.radioes.push(item);
41435         
41436     },
41437     
41438     validate : function()
41439     {   
41440         if(this.getVisibilityEl().hasClass('hidden')){
41441             return true;
41442         }
41443         
41444         var valid = false;
41445         
41446         Roo.each(this.radioes, function(i){
41447             if(!i.checked){
41448                 return;
41449             }
41450             
41451             valid = true;
41452             return false;
41453         });
41454         
41455         if(this.allowBlank) {
41456             return true;
41457         }
41458         
41459         if(this.disabled || valid){
41460             this.markValid();
41461             return true;
41462         }
41463         
41464         this.markInvalid();
41465         return false;
41466         
41467     },
41468     
41469     markValid : function()
41470     {
41471         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41472             this.indicatorEl().removeClass('visible');
41473             this.indicatorEl().addClass('invisible');
41474         }
41475         
41476         
41477         if (Roo.bootstrap.version == 3) {
41478             this.el.removeClass([this.invalidClass, this.validClass]);
41479             this.el.addClass(this.validClass);
41480         } else {
41481             this.el.removeClass(['is-invalid','is-valid']);
41482             this.el.addClass(['is-valid']);
41483         }
41484         this.fireEvent('valid', this);
41485     },
41486     
41487     markInvalid : function(msg)
41488     {
41489         if(this.allowBlank || this.disabled){
41490             return;
41491         }
41492         
41493         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41494             this.indicatorEl().removeClass('invisible');
41495             this.indicatorEl().addClass('visible');
41496         }
41497         if (Roo.bootstrap.version == 3) {
41498             this.el.removeClass([this.invalidClass, this.validClass]);
41499             this.el.addClass(this.invalidClass);
41500         } else {
41501             this.el.removeClass(['is-invalid','is-valid']);
41502             this.el.addClass(['is-invalid']);
41503         }
41504         
41505         this.fireEvent('invalid', this, msg);
41506         
41507     },
41508     
41509     setValue : function(v, suppressEvent)
41510     {   
41511         if(this.value === v){
41512             return;
41513         }
41514         
41515         this.value = v;
41516         
41517         if(this.rendered){
41518             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41519         }
41520         
41521         Roo.each(this.radioes, function(i){
41522             i.checked = false;
41523             i.el.removeClass('checked');
41524         });
41525         
41526         Roo.each(this.radioes, function(i){
41527             
41528             if(i.value === v || i.value.toString() === v.toString()){
41529                 i.checked = true;
41530                 i.el.addClass('checked');
41531                 
41532                 if(suppressEvent !== true){
41533                     this.fireEvent('check', this, i);
41534                 }
41535                 
41536                 return false;
41537             }
41538             
41539         }, this);
41540         
41541         this.validate();
41542     },
41543     
41544     clearInvalid : function(){
41545         
41546         if(!this.el || this.preventMark){
41547             return;
41548         }
41549         
41550         this.el.removeClass([this.invalidClass]);
41551         
41552         this.fireEvent('valid', this);
41553     }
41554     
41555 });
41556
41557 Roo.apply(Roo.bootstrap.form.RadioSet, {
41558     
41559     groups: {},
41560     
41561     register : function(set)
41562     {
41563         this.groups[set.name] = set;
41564     },
41565     
41566     get: function(name) 
41567     {
41568         if (typeof(this.groups[name]) == 'undefined') {
41569             return false;
41570         }
41571         
41572         return this.groups[name] ;
41573     }
41574     
41575 });
41576 /*
41577  * Based on:
41578  * Ext JS Library 1.1.1
41579  * Copyright(c) 2006-2007, Ext JS, LLC.
41580  *
41581  * Originally Released Under LGPL - original licence link has changed is not relivant.
41582  *
41583  * Fork - LGPL
41584  * <script type="text/javascript">
41585  */
41586
41587
41588 /**
41589  * @class Roo.bootstrap.SplitBar
41590  * @extends Roo.util.Observable
41591  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41592  * <br><br>
41593  * Usage:
41594  * <pre><code>
41595 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41596                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41597 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41598 split.minSize = 100;
41599 split.maxSize = 600;
41600 split.animate = true;
41601 split.on('moved', splitterMoved);
41602 </code></pre>
41603  * @constructor
41604  * Create a new SplitBar
41605  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41606  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41607  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41608  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41609                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41610                         position of the SplitBar).
41611  */
41612 Roo.bootstrap.SplitBar = function(cfg){
41613     
41614     /** @private */
41615     
41616     //{
41617     //  dragElement : elm
41618     //  resizingElement: el,
41619         // optional..
41620     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41621     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41622         // existingProxy ???
41623     //}
41624     
41625     this.el = Roo.get(cfg.dragElement, true);
41626     this.el.dom.unselectable = "on";
41627     /** @private */
41628     this.resizingEl = Roo.get(cfg.resizingElement, true);
41629
41630     /**
41631      * @private
41632      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41633      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41634      * @type Number
41635      */
41636     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41637     
41638     /**
41639      * The minimum size of the resizing element. (Defaults to 0)
41640      * @type Number
41641      */
41642     this.minSize = 0;
41643     
41644     /**
41645      * The maximum size of the resizing element. (Defaults to 2000)
41646      * @type Number
41647      */
41648     this.maxSize = 2000;
41649     
41650     /**
41651      * Whether to animate the transition to the new size
41652      * @type Boolean
41653      */
41654     this.animate = false;
41655     
41656     /**
41657      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41658      * @type Boolean
41659      */
41660     this.useShim = false;
41661     
41662     /** @private */
41663     this.shim = null;
41664     
41665     if(!cfg.existingProxy){
41666         /** @private */
41667         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41668     }else{
41669         this.proxy = Roo.get(cfg.existingProxy).dom;
41670     }
41671     /** @private */
41672     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41673     
41674     /** @private */
41675     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41676     
41677     /** @private */
41678     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41679     
41680     /** @private */
41681     this.dragSpecs = {};
41682     
41683     /**
41684      * @private The adapter to use to positon and resize elements
41685      */
41686     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41687     this.adapter.init(this);
41688     
41689     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41690         /** @private */
41691         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41692         this.el.addClass("roo-splitbar-h");
41693     }else{
41694         /** @private */
41695         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41696         this.el.addClass("roo-splitbar-v");
41697     }
41698     
41699     this.addEvents({
41700         /**
41701          * @event resize
41702          * Fires when the splitter is moved (alias for {@link #event-moved})
41703          * @param {Roo.bootstrap.SplitBar} this
41704          * @param {Number} newSize the new width or height
41705          */
41706         "resize" : true,
41707         /**
41708          * @event moved
41709          * Fires when the splitter is moved
41710          * @param {Roo.bootstrap.SplitBar} this
41711          * @param {Number} newSize the new width or height
41712          */
41713         "moved" : true,
41714         /**
41715          * @event beforeresize
41716          * Fires before the splitter is dragged
41717          * @param {Roo.bootstrap.SplitBar} this
41718          */
41719         "beforeresize" : true,
41720
41721         "beforeapply" : true
41722     });
41723
41724     Roo.util.Observable.call(this);
41725 };
41726
41727 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41728     onStartProxyDrag : function(x, y){
41729         this.fireEvent("beforeresize", this);
41730         if(!this.overlay){
41731             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41732             o.unselectable();
41733             o.enableDisplayMode("block");
41734             // all splitbars share the same overlay
41735             Roo.bootstrap.SplitBar.prototype.overlay = o;
41736         }
41737         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41738         this.overlay.show();
41739         Roo.get(this.proxy).setDisplayed("block");
41740         var size = this.adapter.getElementSize(this);
41741         this.activeMinSize = this.getMinimumSize();;
41742         this.activeMaxSize = this.getMaximumSize();;
41743         var c1 = size - this.activeMinSize;
41744         var c2 = Math.max(this.activeMaxSize - size, 0);
41745         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41746             this.dd.resetConstraints();
41747             this.dd.setXConstraint(
41748                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41749                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41750             );
41751             this.dd.setYConstraint(0, 0);
41752         }else{
41753             this.dd.resetConstraints();
41754             this.dd.setXConstraint(0, 0);
41755             this.dd.setYConstraint(
41756                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41757                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41758             );
41759          }
41760         this.dragSpecs.startSize = size;
41761         this.dragSpecs.startPoint = [x, y];
41762         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41763     },
41764     
41765     /** 
41766      * @private Called after the drag operation by the DDProxy
41767      */
41768     onEndProxyDrag : function(e){
41769         Roo.get(this.proxy).setDisplayed(false);
41770         var endPoint = Roo.lib.Event.getXY(e);
41771         if(this.overlay){
41772             this.overlay.hide();
41773         }
41774         var newSize;
41775         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41776             newSize = this.dragSpecs.startSize + 
41777                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41778                     endPoint[0] - this.dragSpecs.startPoint[0] :
41779                     this.dragSpecs.startPoint[0] - endPoint[0]
41780                 );
41781         }else{
41782             newSize = this.dragSpecs.startSize + 
41783                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41784                     endPoint[1] - this.dragSpecs.startPoint[1] :
41785                     this.dragSpecs.startPoint[1] - endPoint[1]
41786                 );
41787         }
41788         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41789         if(newSize != this.dragSpecs.startSize){
41790             if(this.fireEvent('beforeapply', this, newSize) !== false){
41791                 this.adapter.setElementSize(this, newSize);
41792                 this.fireEvent("moved", this, newSize);
41793                 this.fireEvent("resize", this, newSize);
41794             }
41795         }
41796     },
41797     
41798     /**
41799      * Get the adapter this SplitBar uses
41800      * @return The adapter object
41801      */
41802     getAdapter : function(){
41803         return this.adapter;
41804     },
41805     
41806     /**
41807      * Set the adapter this SplitBar uses
41808      * @param {Object} adapter A SplitBar adapter object
41809      */
41810     setAdapter : function(adapter){
41811         this.adapter = adapter;
41812         this.adapter.init(this);
41813     },
41814     
41815     /**
41816      * Gets the minimum size for the resizing element
41817      * @return {Number} The minimum size
41818      */
41819     getMinimumSize : function(){
41820         return this.minSize;
41821     },
41822     
41823     /**
41824      * Sets the minimum size for the resizing element
41825      * @param {Number} minSize The minimum size
41826      */
41827     setMinimumSize : function(minSize){
41828         this.minSize = minSize;
41829     },
41830     
41831     /**
41832      * Gets the maximum size for the resizing element
41833      * @return {Number} The maximum size
41834      */
41835     getMaximumSize : function(){
41836         return this.maxSize;
41837     },
41838     
41839     /**
41840      * Sets the maximum size for the resizing element
41841      * @param {Number} maxSize The maximum size
41842      */
41843     setMaximumSize : function(maxSize){
41844         this.maxSize = maxSize;
41845     },
41846     
41847     /**
41848      * Sets the initialize size for the resizing element
41849      * @param {Number} size The initial size
41850      */
41851     setCurrentSize : function(size){
41852         var oldAnimate = this.animate;
41853         this.animate = false;
41854         this.adapter.setElementSize(this, size);
41855         this.animate = oldAnimate;
41856     },
41857     
41858     /**
41859      * Destroy this splitbar. 
41860      * @param {Boolean} removeEl True to remove the element
41861      */
41862     destroy : function(removeEl){
41863         if(this.shim){
41864             this.shim.remove();
41865         }
41866         this.dd.unreg();
41867         this.proxy.parentNode.removeChild(this.proxy);
41868         if(removeEl){
41869             this.el.remove();
41870         }
41871     }
41872 });
41873
41874 /**
41875  * @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.
41876  */
41877 Roo.bootstrap.SplitBar.createProxy = function(dir){
41878     var proxy = new Roo.Element(document.createElement("div"));
41879     proxy.unselectable();
41880     var cls = 'roo-splitbar-proxy';
41881     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41882     document.body.appendChild(proxy.dom);
41883     return proxy.dom;
41884 };
41885
41886 /** 
41887  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41888  * Default Adapter. It assumes the splitter and resizing element are not positioned
41889  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41890  */
41891 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41892 };
41893
41894 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41895     // do nothing for now
41896     init : function(s){
41897     
41898     },
41899     /**
41900      * Called before drag operations to get the current size of the resizing element. 
41901      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41902      */
41903      getElementSize : function(s){
41904         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41905             return s.resizingEl.getWidth();
41906         }else{
41907             return s.resizingEl.getHeight();
41908         }
41909     },
41910     
41911     /**
41912      * Called after drag operations to set the size of the resizing element.
41913      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41914      * @param {Number} newSize The new size to set
41915      * @param {Function} onComplete A function to be invoked when resizing is complete
41916      */
41917     setElementSize : function(s, newSize, onComplete){
41918         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41919             if(!s.animate){
41920                 s.resizingEl.setWidth(newSize);
41921                 if(onComplete){
41922                     onComplete(s, newSize);
41923                 }
41924             }else{
41925                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41926             }
41927         }else{
41928             
41929             if(!s.animate){
41930                 s.resizingEl.setHeight(newSize);
41931                 if(onComplete){
41932                     onComplete(s, newSize);
41933                 }
41934             }else{
41935                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41936             }
41937         }
41938     }
41939 };
41940
41941 /** 
41942  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41943  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41944  * Adapter that  moves the splitter element to align with the resized sizing element. 
41945  * Used with an absolute positioned SplitBar.
41946  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41947  * document.body, make sure you assign an id to the body element.
41948  */
41949 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41950     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41951     this.container = Roo.get(container);
41952 };
41953
41954 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41955     init : function(s){
41956         this.basic.init(s);
41957     },
41958     
41959     getElementSize : function(s){
41960         return this.basic.getElementSize(s);
41961     },
41962     
41963     setElementSize : function(s, newSize, onComplete){
41964         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41965     },
41966     
41967     moveSplitter : function(s){
41968         var yes = Roo.bootstrap.SplitBar;
41969         switch(s.placement){
41970             case yes.LEFT:
41971                 s.el.setX(s.resizingEl.getRight());
41972                 break;
41973             case yes.RIGHT:
41974                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41975                 break;
41976             case yes.TOP:
41977                 s.el.setY(s.resizingEl.getBottom());
41978                 break;
41979             case yes.BOTTOM:
41980                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41981                 break;
41982         }
41983     }
41984 };
41985
41986 /**
41987  * Orientation constant - Create a vertical SplitBar
41988  * @static
41989  * @type Number
41990  */
41991 Roo.bootstrap.SplitBar.VERTICAL = 1;
41992
41993 /**
41994  * Orientation constant - Create a horizontal SplitBar
41995  * @static
41996  * @type Number
41997  */
41998 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41999
42000 /**
42001  * Placement constant - The resizing element is to the left of the splitter element
42002  * @static
42003  * @type Number
42004  */
42005 Roo.bootstrap.SplitBar.LEFT = 1;
42006
42007 /**
42008  * Placement constant - The resizing element is to the right of the splitter element
42009  * @static
42010  * @type Number
42011  */
42012 Roo.bootstrap.SplitBar.RIGHT = 2;
42013
42014 /**
42015  * Placement constant - The resizing element is positioned above the splitter element
42016  * @static
42017  * @type Number
42018  */
42019 Roo.bootstrap.SplitBar.TOP = 3;
42020
42021 /**
42022  * Placement constant - The resizing element is positioned under splitter element
42023  * @static
42024  * @type Number
42025  */
42026 Roo.bootstrap.SplitBar.BOTTOM = 4;
42027 /*
42028  * Based on:
42029  * Ext JS Library 1.1.1
42030  * Copyright(c) 2006-2007, Ext JS, LLC.
42031  *
42032  * Originally Released Under LGPL - original licence link has changed is not relivant.
42033  *
42034  * Fork - LGPL
42035  * <script type="text/javascript">
42036  */
42037
42038 /**
42039  * @class Roo.bootstrap.layout.Manager
42040  * @extends Roo.bootstrap.Component
42041  * @abstract
42042  * Base class for layout managers.
42043  */
42044 Roo.bootstrap.layout.Manager = function(config)
42045 {
42046     this.monitorWindowResize = true; // do this before we apply configuration.
42047     
42048     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42049
42050
42051
42052
42053
42054     /** false to disable window resize monitoring @type Boolean */
42055     
42056     this.regions = {};
42057     this.addEvents({
42058         /**
42059          * @event layout
42060          * Fires when a layout is performed.
42061          * @param {Roo.LayoutManager} this
42062          */
42063         "layout" : true,
42064         /**
42065          * @event regionresized
42066          * Fires when the user resizes a region.
42067          * @param {Roo.LayoutRegion} region The resized region
42068          * @param {Number} newSize The new size (width for east/west, height for north/south)
42069          */
42070         "regionresized" : true,
42071         /**
42072          * @event regioncollapsed
42073          * Fires when a region is collapsed.
42074          * @param {Roo.LayoutRegion} region The collapsed region
42075          */
42076         "regioncollapsed" : true,
42077         /**
42078          * @event regionexpanded
42079          * Fires when a region is expanded.
42080          * @param {Roo.LayoutRegion} region The expanded region
42081          */
42082         "regionexpanded" : true
42083     });
42084     this.updating = false;
42085
42086     if (config.el) {
42087         this.el = Roo.get(config.el);
42088         this.initEvents();
42089     }
42090
42091 };
42092
42093 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42094
42095
42096     regions : null,
42097
42098     monitorWindowResize : true,
42099
42100
42101     updating : false,
42102
42103
42104     onRender : function(ct, position)
42105     {
42106         if(!this.el){
42107             this.el = Roo.get(ct);
42108             this.initEvents();
42109         }
42110         //this.fireEvent('render',this);
42111     },
42112
42113
42114     initEvents: function()
42115     {
42116
42117
42118         // ie scrollbar fix
42119         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42120             document.body.scroll = "no";
42121         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42122             this.el.position('relative');
42123         }
42124         this.id = this.el.id;
42125         this.el.addClass("roo-layout-container");
42126         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42127         if(this.el.dom != document.body ) {
42128             this.el.on('resize', this.layout,this);
42129             this.el.on('show', this.layout,this);
42130         }
42131
42132     },
42133
42134     /**
42135      * Returns true if this layout is currently being updated
42136      * @return {Boolean}
42137      */
42138     isUpdating : function(){
42139         return this.updating;
42140     },
42141
42142     /**
42143      * Suspend the LayoutManager from doing auto-layouts while
42144      * making multiple add or remove calls
42145      */
42146     beginUpdate : function(){
42147         this.updating = true;
42148     },
42149
42150     /**
42151      * Restore auto-layouts and optionally disable the manager from performing a layout
42152      * @param {Boolean} noLayout true to disable a layout update
42153      */
42154     endUpdate : function(noLayout){
42155         this.updating = false;
42156         if(!noLayout){
42157             this.layout();
42158         }
42159     },
42160
42161     layout: function(){
42162         // abstract...
42163     },
42164
42165     onRegionResized : function(region, newSize){
42166         this.fireEvent("regionresized", region, newSize);
42167         this.layout();
42168     },
42169
42170     onRegionCollapsed : function(region){
42171         this.fireEvent("regioncollapsed", region);
42172     },
42173
42174     onRegionExpanded : function(region){
42175         this.fireEvent("regionexpanded", region);
42176     },
42177
42178     /**
42179      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42180      * performs box-model adjustments.
42181      * @return {Object} The size as an object {width: (the width), height: (the height)}
42182      */
42183     getViewSize : function()
42184     {
42185         var size;
42186         if(this.el.dom != document.body){
42187             size = this.el.getSize();
42188         }else{
42189             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42190         }
42191         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42192         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42193         return size;
42194     },
42195
42196     /**
42197      * Returns the Element this layout is bound to.
42198      * @return {Roo.Element}
42199      */
42200     getEl : function(){
42201         return this.el;
42202     },
42203
42204     /**
42205      * Returns the specified region.
42206      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42207      * @return {Roo.LayoutRegion}
42208      */
42209     getRegion : function(target){
42210         return this.regions[target.toLowerCase()];
42211     },
42212
42213     onWindowResize : function(){
42214         if(this.monitorWindowResize){
42215             this.layout();
42216         }
42217     }
42218 });
42219 /*
42220  * Based on:
42221  * Ext JS Library 1.1.1
42222  * Copyright(c) 2006-2007, Ext JS, LLC.
42223  *
42224  * Originally Released Under LGPL - original licence link has changed is not relivant.
42225  *
42226  * Fork - LGPL
42227  * <script type="text/javascript">
42228  */
42229 /**
42230  * @class Roo.bootstrap.layout.Border
42231  * @extends Roo.bootstrap.layout.Manager
42232  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42233  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42234  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42235  * please see: examples/bootstrap/nested.html<br><br>
42236  
42237 <b>The container the layout is rendered into can be either the body element or any other element.
42238 If it is not the body element, the container needs to either be an absolute positioned element,
42239 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42240 the container size if it is not the body element.</b>
42241
42242 * @constructor
42243 * Create a new Border
42244 * @param {Object} config Configuration options
42245  */
42246 Roo.bootstrap.layout.Border = function(config){
42247     config = config || {};
42248     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42249     
42250     
42251     
42252     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42253         if(config[region]){
42254             config[region].region = region;
42255             this.addRegion(config[region]);
42256         }
42257     },this);
42258     
42259 };
42260
42261 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42262
42263 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42264     
42265         /**
42266          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42267          */
42268         /**
42269          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42270          */
42271         /**
42272          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42273          */
42274         /**
42275          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42276          */
42277         /**
42278          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42279          */
42280         
42281         
42282         
42283         
42284     parent : false, // this might point to a 'nest' or a ???
42285     
42286     /**
42287      * Creates and adds a new region if it doesn't already exist.
42288      * @param {String} target The target region key (north, south, east, west or center).
42289      * @param {Object} config The regions config object
42290      * @return {BorderLayoutRegion} The new region
42291      */
42292     addRegion : function(config)
42293     {
42294         if(!this.regions[config.region]){
42295             var r = this.factory(config);
42296             this.bindRegion(r);
42297         }
42298         return this.regions[config.region];
42299     },
42300
42301     // private (kinda)
42302     bindRegion : function(r){
42303         this.regions[r.config.region] = r;
42304         
42305         r.on("visibilitychange",    this.layout, this);
42306         r.on("paneladded",          this.layout, this);
42307         r.on("panelremoved",        this.layout, this);
42308         r.on("invalidated",         this.layout, this);
42309         r.on("resized",             this.onRegionResized, this);
42310         r.on("collapsed",           this.onRegionCollapsed, this);
42311         r.on("expanded",            this.onRegionExpanded, this);
42312     },
42313
42314     /**
42315      * Performs a layout update.
42316      */
42317     layout : function()
42318     {
42319         if(this.updating) {
42320             return;
42321         }
42322         
42323         // render all the rebions if they have not been done alreayd?
42324         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42325             if(this.regions[region] && !this.regions[region].bodyEl){
42326                 this.regions[region].onRender(this.el)
42327             }
42328         },this);
42329         
42330         var size = this.getViewSize();
42331         var w = size.width;
42332         var h = size.height;
42333         var centerW = w;
42334         var centerH = h;
42335         var centerY = 0;
42336         var centerX = 0;
42337         //var x = 0, y = 0;
42338
42339         var rs = this.regions;
42340         var north = rs["north"];
42341         var south = rs["south"]; 
42342         var west = rs["west"];
42343         var east = rs["east"];
42344         var center = rs["center"];
42345         //if(this.hideOnLayout){ // not supported anymore
42346             //c.el.setStyle("display", "none");
42347         //}
42348         if(north && north.isVisible()){
42349             var b = north.getBox();
42350             var m = north.getMargins();
42351             b.width = w - (m.left+m.right);
42352             b.x = m.left;
42353             b.y = m.top;
42354             centerY = b.height + b.y + m.bottom;
42355             centerH -= centerY;
42356             north.updateBox(this.safeBox(b));
42357         }
42358         if(south && south.isVisible()){
42359             var b = south.getBox();
42360             var m = south.getMargins();
42361             b.width = w - (m.left+m.right);
42362             b.x = m.left;
42363             var totalHeight = (b.height + m.top + m.bottom);
42364             b.y = h - totalHeight + m.top;
42365             centerH -= totalHeight;
42366             south.updateBox(this.safeBox(b));
42367         }
42368         if(west && west.isVisible()){
42369             var b = west.getBox();
42370             var m = west.getMargins();
42371             b.height = centerH - (m.top+m.bottom);
42372             b.x = m.left;
42373             b.y = centerY + m.top;
42374             var totalWidth = (b.width + m.left + m.right);
42375             centerX += totalWidth;
42376             centerW -= totalWidth;
42377             west.updateBox(this.safeBox(b));
42378         }
42379         if(east && east.isVisible()){
42380             var b = east.getBox();
42381             var m = east.getMargins();
42382             b.height = centerH - (m.top+m.bottom);
42383             var totalWidth = (b.width + m.left + m.right);
42384             b.x = w - totalWidth + m.left;
42385             b.y = centerY + m.top;
42386             centerW -= totalWidth;
42387             east.updateBox(this.safeBox(b));
42388         }
42389         if(center){
42390             var m = center.getMargins();
42391             var centerBox = {
42392                 x: centerX + m.left,
42393                 y: centerY + m.top,
42394                 width: centerW - (m.left+m.right),
42395                 height: centerH - (m.top+m.bottom)
42396             };
42397             //if(this.hideOnLayout){
42398                 //center.el.setStyle("display", "block");
42399             //}
42400             center.updateBox(this.safeBox(centerBox));
42401         }
42402         this.el.repaint();
42403         this.fireEvent("layout", this);
42404     },
42405
42406     // private
42407     safeBox : function(box){
42408         box.width = Math.max(0, box.width);
42409         box.height = Math.max(0, box.height);
42410         return box;
42411     },
42412
42413     /**
42414      * Adds a ContentPanel (or subclass) to this layout.
42415      * @param {String} target The target region key (north, south, east, west or center).
42416      * @param {Roo.ContentPanel} panel The panel to add
42417      * @return {Roo.ContentPanel} The added panel
42418      */
42419     add : function(target, panel){
42420          
42421         target = target.toLowerCase();
42422         return this.regions[target].add(panel);
42423     },
42424
42425     /**
42426      * Remove a ContentPanel (or subclass) to this layout.
42427      * @param {String} target The target region key (north, south, east, west or center).
42428      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42429      * @return {Roo.ContentPanel} The removed panel
42430      */
42431     remove : function(target, panel){
42432         target = target.toLowerCase();
42433         return this.regions[target].remove(panel);
42434     },
42435
42436     /**
42437      * Searches all regions for a panel with the specified id
42438      * @param {String} panelId
42439      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42440      */
42441     findPanel : function(panelId){
42442         var rs = this.regions;
42443         for(var target in rs){
42444             if(typeof rs[target] != "function"){
42445                 var p = rs[target].getPanel(panelId);
42446                 if(p){
42447                     return p;
42448                 }
42449             }
42450         }
42451         return null;
42452     },
42453
42454     /**
42455      * Searches all regions for a panel with the specified id and activates (shows) it.
42456      * @param {String/ContentPanel} panelId The panels id or the panel itself
42457      * @return {Roo.ContentPanel} The shown panel or null
42458      */
42459     showPanel : function(panelId) {
42460       var rs = this.regions;
42461       for(var target in rs){
42462          var r = rs[target];
42463          if(typeof r != "function"){
42464             if(r.hasPanel(panelId)){
42465                return r.showPanel(panelId);
42466             }
42467          }
42468       }
42469       return null;
42470    },
42471
42472    /**
42473      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42474      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42475      */
42476    /*
42477     restoreState : function(provider){
42478         if(!provider){
42479             provider = Roo.state.Manager;
42480         }
42481         var sm = new Roo.LayoutStateManager();
42482         sm.init(this, provider);
42483     },
42484 */
42485  
42486  
42487     /**
42488      * Adds a xtype elements to the layout.
42489      * <pre><code>
42490
42491 layout.addxtype({
42492        xtype : 'ContentPanel',
42493        region: 'west',
42494        items: [ .... ]
42495    }
42496 );
42497
42498 layout.addxtype({
42499         xtype : 'NestedLayoutPanel',
42500         region: 'west',
42501         layout: {
42502            center: { },
42503            west: { }   
42504         },
42505         items : [ ... list of content panels or nested layout panels.. ]
42506    }
42507 );
42508 </code></pre>
42509      * @param {Object} cfg Xtype definition of item to add.
42510      */
42511     addxtype : function(cfg)
42512     {
42513         // basically accepts a pannel...
42514         // can accept a layout region..!?!?
42515         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42516         
42517         
42518         // theory?  children can only be panels??
42519         
42520         //if (!cfg.xtype.match(/Panel$/)) {
42521         //    return false;
42522         //}
42523         var ret = false;
42524         
42525         if (typeof(cfg.region) == 'undefined') {
42526             Roo.log("Failed to add Panel, region was not set");
42527             Roo.log(cfg);
42528             return false;
42529         }
42530         var region = cfg.region;
42531         delete cfg.region;
42532         
42533           
42534         var xitems = [];
42535         if (cfg.items) {
42536             xitems = cfg.items;
42537             delete cfg.items;
42538         }
42539         var nb = false;
42540         
42541         if ( region == 'center') {
42542             Roo.log("Center: " + cfg.title);
42543         }
42544         
42545         
42546         switch(cfg.xtype) 
42547         {
42548             case 'Content':  // ContentPanel (el, cfg)
42549             case 'Scroll':  // ContentPanel (el, cfg)
42550             case 'View': 
42551                 cfg.autoCreate = cfg.autoCreate || true;
42552                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42553                 //} else {
42554                 //    var el = this.el.createChild();
42555                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42556                 //}
42557                 
42558                 this.add(region, ret);
42559                 break;
42560             
42561             /*
42562             case 'TreePanel': // our new panel!
42563                 cfg.el = this.el.createChild();
42564                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42565                 this.add(region, ret);
42566                 break;
42567             */
42568             
42569             case 'Nest': 
42570                 // create a new Layout (which is  a Border Layout...
42571                 
42572                 var clayout = cfg.layout;
42573                 clayout.el  = this.el.createChild();
42574                 clayout.items   = clayout.items  || [];
42575                 
42576                 delete cfg.layout;
42577                 
42578                 // replace this exitems with the clayout ones..
42579                 xitems = clayout.items;
42580                  
42581                 // force background off if it's in center...
42582                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42583                     cfg.background = false;
42584                 }
42585                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42586                 
42587                 
42588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42589                 //console.log('adding nested layout panel '  + cfg.toSource());
42590                 this.add(region, ret);
42591                 nb = {}; /// find first...
42592                 break;
42593             
42594             case 'Grid':
42595                 
42596                 // needs grid and region
42597                 
42598                 //var el = this.getRegion(region).el.createChild();
42599                 /*
42600                  *var el = this.el.createChild();
42601                 // create the grid first...
42602                 cfg.grid.container = el;
42603                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42604                 */
42605                 
42606                 if (region == 'center' && this.active ) {
42607                     cfg.background = false;
42608                 }
42609                 
42610                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42611                 
42612                 this.add(region, ret);
42613                 /*
42614                 if (cfg.background) {
42615                     // render grid on panel activation (if panel background)
42616                     ret.on('activate', function(gp) {
42617                         if (!gp.grid.rendered) {
42618                     //        gp.grid.render(el);
42619                         }
42620                     });
42621                 } else {
42622                   //  cfg.grid.render(el);
42623                 }
42624                 */
42625                 break;
42626            
42627            
42628             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42629                 // it was the old xcomponent building that caused this before.
42630                 // espeically if border is the top element in the tree.
42631                 ret = this;
42632                 break; 
42633                 
42634                     
42635                 
42636                 
42637                 
42638             default:
42639                 /*
42640                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42641                     
42642                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42643                     this.add(region, ret);
42644                 } else {
42645                 */
42646                     Roo.log(cfg);
42647                     throw "Can not add '" + cfg.xtype + "' to Border";
42648                     return null;
42649              
42650                                 
42651              
42652         }
42653         this.beginUpdate();
42654         // add children..
42655         var region = '';
42656         var abn = {};
42657         Roo.each(xitems, function(i)  {
42658             region = nb && i.region ? i.region : false;
42659             
42660             var add = ret.addxtype(i);
42661            
42662             if (region) {
42663                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42664                 if (!i.background) {
42665                     abn[region] = nb[region] ;
42666                 }
42667             }
42668             
42669         });
42670         this.endUpdate();
42671
42672         // make the last non-background panel active..
42673         //if (nb) { Roo.log(abn); }
42674         if (nb) {
42675             
42676             for(var r in abn) {
42677                 region = this.getRegion(r);
42678                 if (region) {
42679                     // tried using nb[r], but it does not work..
42680                      
42681                     region.showPanel(abn[r]);
42682                    
42683                 }
42684             }
42685         }
42686         return ret;
42687         
42688     },
42689     
42690     
42691 // private
42692     factory : function(cfg)
42693     {
42694         
42695         var validRegions = Roo.bootstrap.layout.Border.regions;
42696
42697         var target = cfg.region;
42698         cfg.mgr = this;
42699         
42700         var r = Roo.bootstrap.layout;
42701         Roo.log(target);
42702         switch(target){
42703             case "north":
42704                 return new r.North(cfg);
42705             case "south":
42706                 return new r.South(cfg);
42707             case "east":
42708                 return new r.East(cfg);
42709             case "west":
42710                 return new r.West(cfg);
42711             case "center":
42712                 return new r.Center(cfg);
42713         }
42714         throw 'Layout region "'+target+'" not supported.';
42715     }
42716     
42717     
42718 });
42719  /*
42720  * Based on:
42721  * Ext JS Library 1.1.1
42722  * Copyright(c) 2006-2007, Ext JS, LLC.
42723  *
42724  * Originally Released Under LGPL - original licence link has changed is not relivant.
42725  *
42726  * Fork - LGPL
42727  * <script type="text/javascript">
42728  */
42729  
42730 /**
42731  * @class Roo.bootstrap.layout.Basic
42732  * @extends Roo.util.Observable
42733  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42734  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42735  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42736  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42737  * @cfg {string}   region  the region that it inhabits..
42738  * @cfg {bool}   skipConfig skip config?
42739  * 
42740
42741  */
42742 Roo.bootstrap.layout.Basic = function(config){
42743     
42744     this.mgr = config.mgr;
42745     
42746     this.position = config.region;
42747     
42748     var skipConfig = config.skipConfig;
42749     
42750     this.events = {
42751         /**
42752          * @scope Roo.BasicLayoutRegion
42753          */
42754         
42755         /**
42756          * @event beforeremove
42757          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42758          * @param {Roo.LayoutRegion} this
42759          * @param {Roo.ContentPanel} panel The panel
42760          * @param {Object} e The cancel event object
42761          */
42762         "beforeremove" : true,
42763         /**
42764          * @event invalidated
42765          * Fires when the layout for this region is changed.
42766          * @param {Roo.LayoutRegion} this
42767          */
42768         "invalidated" : true,
42769         /**
42770          * @event visibilitychange
42771          * Fires when this region is shown or hidden 
42772          * @param {Roo.LayoutRegion} this
42773          * @param {Boolean} visibility true or false
42774          */
42775         "visibilitychange" : true,
42776         /**
42777          * @event paneladded
42778          * Fires when a panel is added. 
42779          * @param {Roo.LayoutRegion} this
42780          * @param {Roo.ContentPanel} panel The panel
42781          */
42782         "paneladded" : true,
42783         /**
42784          * @event panelremoved
42785          * Fires when a panel is removed. 
42786          * @param {Roo.LayoutRegion} this
42787          * @param {Roo.ContentPanel} panel The panel
42788          */
42789         "panelremoved" : true,
42790         /**
42791          * @event beforecollapse
42792          * Fires when this region before collapse.
42793          * @param {Roo.LayoutRegion} this
42794          */
42795         "beforecollapse" : true,
42796         /**
42797          * @event collapsed
42798          * Fires when this region is collapsed.
42799          * @param {Roo.LayoutRegion} this
42800          */
42801         "collapsed" : true,
42802         /**
42803          * @event expanded
42804          * Fires when this region is expanded.
42805          * @param {Roo.LayoutRegion} this
42806          */
42807         "expanded" : true,
42808         /**
42809          * @event slideshow
42810          * Fires when this region is slid into view.
42811          * @param {Roo.LayoutRegion} this
42812          */
42813         "slideshow" : true,
42814         /**
42815          * @event slidehide
42816          * Fires when this region slides out of view. 
42817          * @param {Roo.LayoutRegion} this
42818          */
42819         "slidehide" : true,
42820         /**
42821          * @event panelactivated
42822          * Fires when a panel is activated. 
42823          * @param {Roo.LayoutRegion} this
42824          * @param {Roo.ContentPanel} panel The activated panel
42825          */
42826         "panelactivated" : true,
42827         /**
42828          * @event resized
42829          * Fires when the user resizes this region. 
42830          * @param {Roo.LayoutRegion} this
42831          * @param {Number} newSize The new size (width for east/west, height for north/south)
42832          */
42833         "resized" : true
42834     };
42835     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42836     this.panels = new Roo.util.MixedCollection();
42837     this.panels.getKey = this.getPanelId.createDelegate(this);
42838     this.box = null;
42839     this.activePanel = null;
42840     // ensure listeners are added...
42841     
42842     if (config.listeners || config.events) {
42843         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42844             listeners : config.listeners || {},
42845             events : config.events || {}
42846         });
42847     }
42848     
42849     if(skipConfig !== true){
42850         this.applyConfig(config);
42851     }
42852 };
42853
42854 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42855 {
42856     getPanelId : function(p){
42857         return p.getId();
42858     },
42859     
42860     applyConfig : function(config){
42861         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42862         this.config = config;
42863         
42864     },
42865     
42866     /**
42867      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42868      * the width, for horizontal (north, south) the height.
42869      * @param {Number} newSize The new width or height
42870      */
42871     resizeTo : function(newSize){
42872         var el = this.el ? this.el :
42873                  (this.activePanel ? this.activePanel.getEl() : null);
42874         if(el){
42875             switch(this.position){
42876                 case "east":
42877                 case "west":
42878                     el.setWidth(newSize);
42879                     this.fireEvent("resized", this, newSize);
42880                 break;
42881                 case "north":
42882                 case "south":
42883                     el.setHeight(newSize);
42884                     this.fireEvent("resized", this, newSize);
42885                 break;                
42886             }
42887         }
42888     },
42889     
42890     getBox : function(){
42891         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42892     },
42893     
42894     getMargins : function(){
42895         return this.margins;
42896     },
42897     
42898     updateBox : function(box){
42899         this.box = box;
42900         var el = this.activePanel.getEl();
42901         el.dom.style.left = box.x + "px";
42902         el.dom.style.top = box.y + "px";
42903         this.activePanel.setSize(box.width, box.height);
42904     },
42905     
42906     /**
42907      * Returns the container element for this region.
42908      * @return {Roo.Element}
42909      */
42910     getEl : function(){
42911         return this.activePanel;
42912     },
42913     
42914     /**
42915      * Returns true if this region is currently visible.
42916      * @return {Boolean}
42917      */
42918     isVisible : function(){
42919         return this.activePanel ? true : false;
42920     },
42921     
42922     setActivePanel : function(panel){
42923         panel = this.getPanel(panel);
42924         if(this.activePanel && this.activePanel != panel){
42925             this.activePanel.setActiveState(false);
42926             this.activePanel.getEl().setLeftTop(-10000,-10000);
42927         }
42928         this.activePanel = panel;
42929         panel.setActiveState(true);
42930         if(this.box){
42931             panel.setSize(this.box.width, this.box.height);
42932         }
42933         this.fireEvent("panelactivated", this, panel);
42934         this.fireEvent("invalidated");
42935     },
42936     
42937     /**
42938      * Show the specified panel.
42939      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42940      * @return {Roo.ContentPanel} The shown panel or null
42941      */
42942     showPanel : function(panel){
42943         panel = this.getPanel(panel);
42944         if(panel){
42945             this.setActivePanel(panel);
42946         }
42947         return panel;
42948     },
42949     
42950     /**
42951      * Get the active panel for this region.
42952      * @return {Roo.ContentPanel} The active panel or null
42953      */
42954     getActivePanel : function(){
42955         return this.activePanel;
42956     },
42957     
42958     /**
42959      * Add the passed ContentPanel(s)
42960      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42961      * @return {Roo.ContentPanel} The panel added (if only one was added)
42962      */
42963     add : function(panel){
42964         if(arguments.length > 1){
42965             for(var i = 0, len = arguments.length; i < len; i++) {
42966                 this.add(arguments[i]);
42967             }
42968             return null;
42969         }
42970         if(this.hasPanel(panel)){
42971             this.showPanel(panel);
42972             return panel;
42973         }
42974         var el = panel.getEl();
42975         if(el.dom.parentNode != this.mgr.el.dom){
42976             this.mgr.el.dom.appendChild(el.dom);
42977         }
42978         if(panel.setRegion){
42979             panel.setRegion(this);
42980         }
42981         this.panels.add(panel);
42982         el.setStyle("position", "absolute");
42983         if(!panel.background){
42984             this.setActivePanel(panel);
42985             if(this.config.initialSize && this.panels.getCount()==1){
42986                 this.resizeTo(this.config.initialSize);
42987             }
42988         }
42989         this.fireEvent("paneladded", this, panel);
42990         return panel;
42991     },
42992     
42993     /**
42994      * Returns true if the panel is in this region.
42995      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42996      * @return {Boolean}
42997      */
42998     hasPanel : function(panel){
42999         if(typeof panel == "object"){ // must be panel obj
43000             panel = panel.getId();
43001         }
43002         return this.getPanel(panel) ? true : false;
43003     },
43004     
43005     /**
43006      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43007      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43008      * @param {Boolean} preservePanel Overrides the config preservePanel option
43009      * @return {Roo.ContentPanel} The panel that was removed
43010      */
43011     remove : function(panel, preservePanel){
43012         panel = this.getPanel(panel);
43013         if(!panel){
43014             return null;
43015         }
43016         var e = {};
43017         this.fireEvent("beforeremove", this, panel, e);
43018         if(e.cancel === true){
43019             return null;
43020         }
43021         var panelId = panel.getId();
43022         this.panels.removeKey(panelId);
43023         return panel;
43024     },
43025     
43026     /**
43027      * Returns the panel specified or null if it's not in this region.
43028      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43029      * @return {Roo.ContentPanel}
43030      */
43031     getPanel : function(id){
43032         if(typeof id == "object"){ // must be panel obj
43033             return id;
43034         }
43035         return this.panels.get(id);
43036     },
43037     
43038     /**
43039      * Returns this regions position (north/south/east/west/center).
43040      * @return {String} 
43041      */
43042     getPosition: function(){
43043         return this.position;    
43044     }
43045 });/*
43046  * Based on:
43047  * Ext JS Library 1.1.1
43048  * Copyright(c) 2006-2007, Ext JS, LLC.
43049  *
43050  * Originally Released Under LGPL - original licence link has changed is not relivant.
43051  *
43052  * Fork - LGPL
43053  * <script type="text/javascript">
43054  */
43055  
43056 /**
43057  * @class Roo.bootstrap.layout.Region
43058  * @extends Roo.bootstrap.layout.Basic
43059  * This class represents a region in a layout manager.
43060  
43061  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43062  * @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})
43063  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43064  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43065  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43066  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43067  * @cfg {String}    title           The title for the region (overrides panel titles)
43068  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43069  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43070  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43071  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43072  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43073  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43074  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43075  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43076  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43077  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43078
43079  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43080  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43081  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43082  * @cfg {Number}    width           For East/West panels
43083  * @cfg {Number}    height          For North/South panels
43084  * @cfg {Boolean}   split           To show the splitter
43085  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43086  * 
43087  * @cfg {string}   cls             Extra CSS classes to add to region
43088  * 
43089  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43090  * @cfg {string}   region  the region that it inhabits..
43091  *
43092
43093  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43094  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43095
43096  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43097  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43098  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43099  */
43100 Roo.bootstrap.layout.Region = function(config)
43101 {
43102     this.applyConfig(config);
43103
43104     var mgr = config.mgr;
43105     var pos = config.region;
43106     config.skipConfig = true;
43107     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43108     
43109     if (mgr.el) {
43110         this.onRender(mgr.el);   
43111     }
43112      
43113     this.visible = true;
43114     this.collapsed = false;
43115     this.unrendered_panels = [];
43116 };
43117
43118 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43119
43120     position: '', // set by wrapper (eg. north/south etc..)
43121     unrendered_panels : null,  // unrendered panels.
43122     
43123     tabPosition : false,
43124     
43125     mgr: false, // points to 'Border'
43126     
43127     
43128     createBody : function(){
43129         /** This region's body element 
43130         * @type Roo.Element */
43131         this.bodyEl = this.el.createChild({
43132                 tag: "div",
43133                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43134         });
43135     },
43136
43137     onRender: function(ctr, pos)
43138     {
43139         var dh = Roo.DomHelper;
43140         /** This region's container element 
43141         * @type Roo.Element */
43142         this.el = dh.append(ctr.dom, {
43143                 tag: "div",
43144                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43145             }, true);
43146         /** This region's title element 
43147         * @type Roo.Element */
43148     
43149         this.titleEl = dh.append(this.el.dom,  {
43150                 tag: "div",
43151                 unselectable: "on",
43152                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43153                 children:[
43154                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43155                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43156                 ]
43157             }, true);
43158         
43159         this.titleEl.enableDisplayMode();
43160         /** This region's title text element 
43161         * @type HTMLElement */
43162         this.titleTextEl = this.titleEl.dom.firstChild;
43163         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43164         /*
43165         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43166         this.closeBtn.enableDisplayMode();
43167         this.closeBtn.on("click", this.closeClicked, this);
43168         this.closeBtn.hide();
43169     */
43170         this.createBody(this.config);
43171         if(this.config.hideWhenEmpty){
43172             this.hide();
43173             this.on("paneladded", this.validateVisibility, this);
43174             this.on("panelremoved", this.validateVisibility, this);
43175         }
43176         if(this.autoScroll){
43177             this.bodyEl.setStyle("overflow", "auto");
43178         }else{
43179             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43180         }
43181         //if(c.titlebar !== false){
43182             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43183                 this.titleEl.hide();
43184             }else{
43185                 this.titleEl.show();
43186                 if(this.config.title){
43187                     this.titleTextEl.innerHTML = this.config.title;
43188                 }
43189             }
43190         //}
43191         if(this.config.collapsed){
43192             this.collapse(true);
43193         }
43194         if(this.config.hidden){
43195             this.hide();
43196         }
43197         
43198         if (this.unrendered_panels && this.unrendered_panels.length) {
43199             for (var i =0;i< this.unrendered_panels.length; i++) {
43200                 this.add(this.unrendered_panels[i]);
43201             }
43202             this.unrendered_panels = null;
43203             
43204         }
43205         
43206     },
43207     
43208     applyConfig : function(c)
43209     {
43210         /*
43211          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43212             var dh = Roo.DomHelper;
43213             if(c.titlebar !== false){
43214                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43215                 this.collapseBtn.on("click", this.collapse, this);
43216                 this.collapseBtn.enableDisplayMode();
43217                 /*
43218                 if(c.showPin === true || this.showPin){
43219                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43220                     this.stickBtn.enableDisplayMode();
43221                     this.stickBtn.on("click", this.expand, this);
43222                     this.stickBtn.hide();
43223                 }
43224                 
43225             }
43226             */
43227             /** This region's collapsed element
43228             * @type Roo.Element */
43229             /*
43230              *
43231             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43232                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43233             ]}, true);
43234             
43235             if(c.floatable !== false){
43236                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43237                this.collapsedEl.on("click", this.collapseClick, this);
43238             }
43239
43240             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43241                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43242                    id: "message", unselectable: "on", style:{"float":"left"}});
43243                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43244              }
43245             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43246             this.expandBtn.on("click", this.expand, this);
43247             
43248         }
43249         
43250         if(this.collapseBtn){
43251             this.collapseBtn.setVisible(c.collapsible == true);
43252         }
43253         
43254         this.cmargins = c.cmargins || this.cmargins ||
43255                          (this.position == "west" || this.position == "east" ?
43256                              {top: 0, left: 2, right:2, bottom: 0} :
43257                              {top: 2, left: 0, right:0, bottom: 2});
43258         */
43259         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43260         
43261         
43262         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43263         
43264         this.autoScroll = c.autoScroll || false;
43265         
43266         
43267        
43268         
43269         this.duration = c.duration || .30;
43270         this.slideDuration = c.slideDuration || .45;
43271         this.config = c;
43272        
43273     },
43274     /**
43275      * Returns true if this region is currently visible.
43276      * @return {Boolean}
43277      */
43278     isVisible : function(){
43279         return this.visible;
43280     },
43281
43282     /**
43283      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43284      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43285      */
43286     //setCollapsedTitle : function(title){
43287     //    title = title || "&#160;";
43288      //   if(this.collapsedTitleTextEl){
43289       //      this.collapsedTitleTextEl.innerHTML = title;
43290        // }
43291     //},
43292
43293     getBox : function(){
43294         var b;
43295       //  if(!this.collapsed){
43296             b = this.el.getBox(false, true);
43297        // }else{
43298           //  b = this.collapsedEl.getBox(false, true);
43299         //}
43300         return b;
43301     },
43302
43303     getMargins : function(){
43304         return this.margins;
43305         //return this.collapsed ? this.cmargins : this.margins;
43306     },
43307 /*
43308     highlight : function(){
43309         this.el.addClass("x-layout-panel-dragover");
43310     },
43311
43312     unhighlight : function(){
43313         this.el.removeClass("x-layout-panel-dragover");
43314     },
43315 */
43316     updateBox : function(box)
43317     {
43318         if (!this.bodyEl) {
43319             return; // not rendered yet..
43320         }
43321         
43322         this.box = box;
43323         if(!this.collapsed){
43324             this.el.dom.style.left = box.x + "px";
43325             this.el.dom.style.top = box.y + "px";
43326             this.updateBody(box.width, box.height);
43327         }else{
43328             this.collapsedEl.dom.style.left = box.x + "px";
43329             this.collapsedEl.dom.style.top = box.y + "px";
43330             this.collapsedEl.setSize(box.width, box.height);
43331         }
43332         if(this.tabs){
43333             this.tabs.autoSizeTabs();
43334         }
43335     },
43336
43337     updateBody : function(w, h)
43338     {
43339         if(w !== null){
43340             this.el.setWidth(w);
43341             w -= this.el.getBorderWidth("rl");
43342             if(this.config.adjustments){
43343                 w += this.config.adjustments[0];
43344             }
43345         }
43346         if(h !== null && h > 0){
43347             this.el.setHeight(h);
43348             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43349             h -= this.el.getBorderWidth("tb");
43350             if(this.config.adjustments){
43351                 h += this.config.adjustments[1];
43352             }
43353             this.bodyEl.setHeight(h);
43354             if(this.tabs){
43355                 h = this.tabs.syncHeight(h);
43356             }
43357         }
43358         if(this.panelSize){
43359             w = w !== null ? w : this.panelSize.width;
43360             h = h !== null ? h : this.panelSize.height;
43361         }
43362         if(this.activePanel){
43363             var el = this.activePanel.getEl();
43364             w = w !== null ? w : el.getWidth();
43365             h = h !== null ? h : el.getHeight();
43366             this.panelSize = {width: w, height: h};
43367             this.activePanel.setSize(w, h);
43368         }
43369         if(Roo.isIE && this.tabs){
43370             this.tabs.el.repaint();
43371         }
43372     },
43373
43374     /**
43375      * Returns the container element for this region.
43376      * @return {Roo.Element}
43377      */
43378     getEl : function(){
43379         return this.el;
43380     },
43381
43382     /**
43383      * Hides this region.
43384      */
43385     hide : function(){
43386         //if(!this.collapsed){
43387             this.el.dom.style.left = "-2000px";
43388             this.el.hide();
43389         //}else{
43390          //   this.collapsedEl.dom.style.left = "-2000px";
43391          //   this.collapsedEl.hide();
43392        // }
43393         this.visible = false;
43394         this.fireEvent("visibilitychange", this, false);
43395     },
43396
43397     /**
43398      * Shows this region if it was previously hidden.
43399      */
43400     show : function(){
43401         //if(!this.collapsed){
43402             this.el.show();
43403         //}else{
43404         //    this.collapsedEl.show();
43405        // }
43406         this.visible = true;
43407         this.fireEvent("visibilitychange", this, true);
43408     },
43409 /*
43410     closeClicked : function(){
43411         if(this.activePanel){
43412             this.remove(this.activePanel);
43413         }
43414     },
43415
43416     collapseClick : function(e){
43417         if(this.isSlid){
43418            e.stopPropagation();
43419            this.slideIn();
43420         }else{
43421            e.stopPropagation();
43422            this.slideOut();
43423         }
43424     },
43425 */
43426     /**
43427      * Collapses this region.
43428      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43429      */
43430     /*
43431     collapse : function(skipAnim, skipCheck = false){
43432         if(this.collapsed) {
43433             return;
43434         }
43435         
43436         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43437             
43438             this.collapsed = true;
43439             if(this.split){
43440                 this.split.el.hide();
43441             }
43442             if(this.config.animate && skipAnim !== true){
43443                 this.fireEvent("invalidated", this);
43444                 this.animateCollapse();
43445             }else{
43446                 this.el.setLocation(-20000,-20000);
43447                 this.el.hide();
43448                 this.collapsedEl.show();
43449                 this.fireEvent("collapsed", this);
43450                 this.fireEvent("invalidated", this);
43451             }
43452         }
43453         
43454     },
43455 */
43456     animateCollapse : function(){
43457         // overridden
43458     },
43459
43460     /**
43461      * Expands this region if it was previously collapsed.
43462      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43463      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43464      */
43465     /*
43466     expand : function(e, skipAnim){
43467         if(e) {
43468             e.stopPropagation();
43469         }
43470         if(!this.collapsed || this.el.hasActiveFx()) {
43471             return;
43472         }
43473         if(this.isSlid){
43474             this.afterSlideIn();
43475             skipAnim = true;
43476         }
43477         this.collapsed = false;
43478         if(this.config.animate && skipAnim !== true){
43479             this.animateExpand();
43480         }else{
43481             this.el.show();
43482             if(this.split){
43483                 this.split.el.show();
43484             }
43485             this.collapsedEl.setLocation(-2000,-2000);
43486             this.collapsedEl.hide();
43487             this.fireEvent("invalidated", this);
43488             this.fireEvent("expanded", this);
43489         }
43490     },
43491 */
43492     animateExpand : function(){
43493         // overridden
43494     },
43495
43496     initTabs : function()
43497     {
43498         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43499         
43500         var ts = new Roo.bootstrap.panel.Tabs({
43501             el: this.bodyEl.dom,
43502             region : this,
43503             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43504             disableTooltips: this.config.disableTabTips,
43505             toolbar : this.config.toolbar
43506         });
43507         
43508         if(this.config.hideTabs){
43509             ts.stripWrap.setDisplayed(false);
43510         }
43511         this.tabs = ts;
43512         ts.resizeTabs = this.config.resizeTabs === true;
43513         ts.minTabWidth = this.config.minTabWidth || 40;
43514         ts.maxTabWidth = this.config.maxTabWidth || 250;
43515         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43516         ts.monitorResize = false;
43517         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43518         ts.bodyEl.addClass('roo-layout-tabs-body');
43519         this.panels.each(this.initPanelAsTab, this);
43520     },
43521
43522     initPanelAsTab : function(panel){
43523         var ti = this.tabs.addTab(
43524             panel.getEl().id,
43525             panel.getTitle(),
43526             null,
43527             this.config.closeOnTab && panel.isClosable(),
43528             panel.tpl
43529         );
43530         if(panel.tabTip !== undefined){
43531             ti.setTooltip(panel.tabTip);
43532         }
43533         ti.on("activate", function(){
43534               this.setActivePanel(panel);
43535         }, this);
43536         
43537         if(this.config.closeOnTab){
43538             ti.on("beforeclose", function(t, e){
43539                 e.cancel = true;
43540                 this.remove(panel);
43541             }, this);
43542         }
43543         
43544         panel.tabItem = ti;
43545         
43546         return ti;
43547     },
43548
43549     updatePanelTitle : function(panel, title)
43550     {
43551         if(this.activePanel == panel){
43552             this.updateTitle(title);
43553         }
43554         if(this.tabs){
43555             var ti = this.tabs.getTab(panel.getEl().id);
43556             ti.setText(title);
43557             if(panel.tabTip !== undefined){
43558                 ti.setTooltip(panel.tabTip);
43559             }
43560         }
43561     },
43562
43563     updateTitle : function(title){
43564         if(this.titleTextEl && !this.config.title){
43565             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43566         }
43567     },
43568
43569     setActivePanel : function(panel)
43570     {
43571         panel = this.getPanel(panel);
43572         if(this.activePanel && this.activePanel != panel){
43573             if(this.activePanel.setActiveState(false) === false){
43574                 return;
43575             }
43576         }
43577         this.activePanel = panel;
43578         panel.setActiveState(true);
43579         if(this.panelSize){
43580             panel.setSize(this.panelSize.width, this.panelSize.height);
43581         }
43582         if(this.closeBtn){
43583             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43584         }
43585         this.updateTitle(panel.getTitle());
43586         if(this.tabs){
43587             this.fireEvent("invalidated", this);
43588         }
43589         this.fireEvent("panelactivated", this, panel);
43590     },
43591
43592     /**
43593      * Shows the specified panel.
43594      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43595      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43596      */
43597     showPanel : function(panel)
43598     {
43599         panel = this.getPanel(panel);
43600         if(panel){
43601             if(this.tabs){
43602                 var tab = this.tabs.getTab(panel.getEl().id);
43603                 if(tab.isHidden()){
43604                     this.tabs.unhideTab(tab.id);
43605                 }
43606                 tab.activate();
43607             }else{
43608                 this.setActivePanel(panel);
43609             }
43610         }
43611         return panel;
43612     },
43613
43614     /**
43615      * Get the active panel for this region.
43616      * @return {Roo.ContentPanel} The active panel or null
43617      */
43618     getActivePanel : function(){
43619         return this.activePanel;
43620     },
43621
43622     validateVisibility : function(){
43623         if(this.panels.getCount() < 1){
43624             this.updateTitle("&#160;");
43625             this.closeBtn.hide();
43626             this.hide();
43627         }else{
43628             if(!this.isVisible()){
43629                 this.show();
43630             }
43631         }
43632     },
43633
43634     /**
43635      * Adds the passed ContentPanel(s) to this region.
43636      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43637      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43638      */
43639     add : function(panel)
43640     {
43641         if(arguments.length > 1){
43642             for(var i = 0, len = arguments.length; i < len; i++) {
43643                 this.add(arguments[i]);
43644             }
43645             return null;
43646         }
43647         
43648         // if we have not been rendered yet, then we can not really do much of this..
43649         if (!this.bodyEl) {
43650             this.unrendered_panels.push(panel);
43651             return panel;
43652         }
43653         
43654         
43655         
43656         
43657         if(this.hasPanel(panel)){
43658             this.showPanel(panel);
43659             return panel;
43660         }
43661         panel.setRegion(this);
43662         this.panels.add(panel);
43663        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43664             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43665             // and hide them... ???
43666             this.bodyEl.dom.appendChild(panel.getEl().dom);
43667             if(panel.background !== true){
43668                 this.setActivePanel(panel);
43669             }
43670             this.fireEvent("paneladded", this, panel);
43671             return panel;
43672         }
43673         */
43674         if(!this.tabs){
43675             this.initTabs();
43676         }else{
43677             this.initPanelAsTab(panel);
43678         }
43679         
43680         
43681         if(panel.background !== true){
43682             this.tabs.activate(panel.getEl().id);
43683         }
43684         this.fireEvent("paneladded", this, panel);
43685         return panel;
43686     },
43687
43688     /**
43689      * Hides the tab for the specified panel.
43690      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43691      */
43692     hidePanel : function(panel){
43693         if(this.tabs && (panel = this.getPanel(panel))){
43694             this.tabs.hideTab(panel.getEl().id);
43695         }
43696     },
43697
43698     /**
43699      * Unhides the tab for a previously hidden panel.
43700      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43701      */
43702     unhidePanel : function(panel){
43703         if(this.tabs && (panel = this.getPanel(panel))){
43704             this.tabs.unhideTab(panel.getEl().id);
43705         }
43706     },
43707
43708     clearPanels : function(){
43709         while(this.panels.getCount() > 0){
43710              this.remove(this.panels.first());
43711         }
43712     },
43713
43714     /**
43715      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43717      * @param {Boolean} preservePanel Overrides the config preservePanel option
43718      * @return {Roo.ContentPanel} The panel that was removed
43719      */
43720     remove : function(panel, preservePanel)
43721     {
43722         panel = this.getPanel(panel);
43723         if(!panel){
43724             return null;
43725         }
43726         var e = {};
43727         this.fireEvent("beforeremove", this, panel, e);
43728         if(e.cancel === true){
43729             return null;
43730         }
43731         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43732         var panelId = panel.getId();
43733         this.panels.removeKey(panelId);
43734         if(preservePanel){
43735             document.body.appendChild(panel.getEl().dom);
43736         }
43737         if(this.tabs){
43738             this.tabs.removeTab(panel.getEl().id);
43739         }else if (!preservePanel){
43740             this.bodyEl.dom.removeChild(panel.getEl().dom);
43741         }
43742         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43743             var p = this.panels.first();
43744             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43745             tempEl.appendChild(p.getEl().dom);
43746             this.bodyEl.update("");
43747             this.bodyEl.dom.appendChild(p.getEl().dom);
43748             tempEl = null;
43749             this.updateTitle(p.getTitle());
43750             this.tabs = null;
43751             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43752             this.setActivePanel(p);
43753         }
43754         panel.setRegion(null);
43755         if(this.activePanel == panel){
43756             this.activePanel = null;
43757         }
43758         if(this.config.autoDestroy !== false && preservePanel !== true){
43759             try{panel.destroy();}catch(e){}
43760         }
43761         this.fireEvent("panelremoved", this, panel);
43762         return panel;
43763     },
43764
43765     /**
43766      * Returns the TabPanel component used by this region
43767      * @return {Roo.TabPanel}
43768      */
43769     getTabs : function(){
43770         return this.tabs;
43771     },
43772
43773     createTool : function(parentEl, className){
43774         var btn = Roo.DomHelper.append(parentEl, {
43775             tag: "div",
43776             cls: "x-layout-tools-button",
43777             children: [ {
43778                 tag: "div",
43779                 cls: "roo-layout-tools-button-inner " + className,
43780                 html: "&#160;"
43781             }]
43782         }, true);
43783         btn.addClassOnOver("roo-layout-tools-button-over");
43784         return btn;
43785     }
43786 });/*
43787  * Based on:
43788  * Ext JS Library 1.1.1
43789  * Copyright(c) 2006-2007, Ext JS, LLC.
43790  *
43791  * Originally Released Under LGPL - original licence link has changed is not relivant.
43792  *
43793  * Fork - LGPL
43794  * <script type="text/javascript">
43795  */
43796  
43797
43798
43799 /**
43800  * @class Roo.SplitLayoutRegion
43801  * @extends Roo.LayoutRegion
43802  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43803  */
43804 Roo.bootstrap.layout.Split = function(config){
43805     this.cursor = config.cursor;
43806     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43807 };
43808
43809 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43810 {
43811     splitTip : "Drag to resize.",
43812     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43813     useSplitTips : false,
43814
43815     applyConfig : function(config){
43816         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43817     },
43818     
43819     onRender : function(ctr,pos) {
43820         
43821         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43822         if(!this.config.split){
43823             return;
43824         }
43825         if(!this.split){
43826             
43827             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43828                             tag: "div",
43829                             id: this.el.id + "-split",
43830                             cls: "roo-layout-split roo-layout-split-"+this.position,
43831                             html: "&#160;"
43832             });
43833             /** The SplitBar for this region 
43834             * @type Roo.SplitBar */
43835             // does not exist yet...
43836             Roo.log([this.position, this.orientation]);
43837             
43838             this.split = new Roo.bootstrap.SplitBar({
43839                 dragElement : splitEl,
43840                 resizingElement: this.el,
43841                 orientation : this.orientation
43842             });
43843             
43844             this.split.on("moved", this.onSplitMove, this);
43845             this.split.useShim = this.config.useShim === true;
43846             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43847             if(this.useSplitTips){
43848                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43849             }
43850             //if(config.collapsible){
43851             //    this.split.el.on("dblclick", this.collapse,  this);
43852             //}
43853         }
43854         if(typeof this.config.minSize != "undefined"){
43855             this.split.minSize = this.config.minSize;
43856         }
43857         if(typeof this.config.maxSize != "undefined"){
43858             this.split.maxSize = this.config.maxSize;
43859         }
43860         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43861             this.hideSplitter();
43862         }
43863         
43864     },
43865
43866     getHMaxSize : function(){
43867          var cmax = this.config.maxSize || 10000;
43868          var center = this.mgr.getRegion("center");
43869          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43870     },
43871
43872     getVMaxSize : function(){
43873          var cmax = this.config.maxSize || 10000;
43874          var center = this.mgr.getRegion("center");
43875          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43876     },
43877
43878     onSplitMove : function(split, newSize){
43879         this.fireEvent("resized", this, newSize);
43880     },
43881     
43882     /** 
43883      * Returns the {@link Roo.SplitBar} for this region.
43884      * @return {Roo.SplitBar}
43885      */
43886     getSplitBar : function(){
43887         return this.split;
43888     },
43889     
43890     hide : function(){
43891         this.hideSplitter();
43892         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43893     },
43894
43895     hideSplitter : function(){
43896         if(this.split){
43897             this.split.el.setLocation(-2000,-2000);
43898             this.split.el.hide();
43899         }
43900     },
43901
43902     show : function(){
43903         if(this.split){
43904             this.split.el.show();
43905         }
43906         Roo.bootstrap.layout.Split.superclass.show.call(this);
43907     },
43908     
43909     beforeSlide: function(){
43910         if(Roo.isGecko){// firefox overflow auto bug workaround
43911             this.bodyEl.clip();
43912             if(this.tabs) {
43913                 this.tabs.bodyEl.clip();
43914             }
43915             if(this.activePanel){
43916                 this.activePanel.getEl().clip();
43917                 
43918                 if(this.activePanel.beforeSlide){
43919                     this.activePanel.beforeSlide();
43920                 }
43921             }
43922         }
43923     },
43924     
43925     afterSlide : function(){
43926         if(Roo.isGecko){// firefox overflow auto bug workaround
43927             this.bodyEl.unclip();
43928             if(this.tabs) {
43929                 this.tabs.bodyEl.unclip();
43930             }
43931             if(this.activePanel){
43932                 this.activePanel.getEl().unclip();
43933                 if(this.activePanel.afterSlide){
43934                     this.activePanel.afterSlide();
43935                 }
43936             }
43937         }
43938     },
43939
43940     initAutoHide : function(){
43941         if(this.autoHide !== false){
43942             if(!this.autoHideHd){
43943                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43944                 this.autoHideHd = {
43945                     "mouseout": function(e){
43946                         if(!e.within(this.el, true)){
43947                             st.delay(500);
43948                         }
43949                     },
43950                     "mouseover" : function(e){
43951                         st.cancel();
43952                     },
43953                     scope : this
43954                 };
43955             }
43956             this.el.on(this.autoHideHd);
43957         }
43958     },
43959
43960     clearAutoHide : function(){
43961         if(this.autoHide !== false){
43962             this.el.un("mouseout", this.autoHideHd.mouseout);
43963             this.el.un("mouseover", this.autoHideHd.mouseover);
43964         }
43965     },
43966
43967     clearMonitor : function(){
43968         Roo.get(document).un("click", this.slideInIf, this);
43969     },
43970
43971     // these names are backwards but not changed for compat
43972     slideOut : function(){
43973         if(this.isSlid || this.el.hasActiveFx()){
43974             return;
43975         }
43976         this.isSlid = true;
43977         if(this.collapseBtn){
43978             this.collapseBtn.hide();
43979         }
43980         this.closeBtnState = this.closeBtn.getStyle('display');
43981         this.closeBtn.hide();
43982         if(this.stickBtn){
43983             this.stickBtn.show();
43984         }
43985         this.el.show();
43986         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43987         this.beforeSlide();
43988         this.el.setStyle("z-index", 10001);
43989         this.el.slideIn(this.getSlideAnchor(), {
43990             callback: function(){
43991                 this.afterSlide();
43992                 this.initAutoHide();
43993                 Roo.get(document).on("click", this.slideInIf, this);
43994                 this.fireEvent("slideshow", this);
43995             },
43996             scope: this,
43997             block: true
43998         });
43999     },
44000
44001     afterSlideIn : function(){
44002         this.clearAutoHide();
44003         this.isSlid = false;
44004         this.clearMonitor();
44005         this.el.setStyle("z-index", "");
44006         if(this.collapseBtn){
44007             this.collapseBtn.show();
44008         }
44009         this.closeBtn.setStyle('display', this.closeBtnState);
44010         if(this.stickBtn){
44011             this.stickBtn.hide();
44012         }
44013         this.fireEvent("slidehide", this);
44014     },
44015
44016     slideIn : function(cb){
44017         if(!this.isSlid || this.el.hasActiveFx()){
44018             Roo.callback(cb);
44019             return;
44020         }
44021         this.isSlid = false;
44022         this.beforeSlide();
44023         this.el.slideOut(this.getSlideAnchor(), {
44024             callback: function(){
44025                 this.el.setLeftTop(-10000, -10000);
44026                 this.afterSlide();
44027                 this.afterSlideIn();
44028                 Roo.callback(cb);
44029             },
44030             scope: this,
44031             block: true
44032         });
44033     },
44034     
44035     slideInIf : function(e){
44036         if(!e.within(this.el)){
44037             this.slideIn();
44038         }
44039     },
44040
44041     animateCollapse : function(){
44042         this.beforeSlide();
44043         this.el.setStyle("z-index", 20000);
44044         var anchor = this.getSlideAnchor();
44045         this.el.slideOut(anchor, {
44046             callback : function(){
44047                 this.el.setStyle("z-index", "");
44048                 this.collapsedEl.slideIn(anchor, {duration:.3});
44049                 this.afterSlide();
44050                 this.el.setLocation(-10000,-10000);
44051                 this.el.hide();
44052                 this.fireEvent("collapsed", this);
44053             },
44054             scope: this,
44055             block: true
44056         });
44057     },
44058
44059     animateExpand : function(){
44060         this.beforeSlide();
44061         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44062         this.el.setStyle("z-index", 20000);
44063         this.collapsedEl.hide({
44064             duration:.1
44065         });
44066         this.el.slideIn(this.getSlideAnchor(), {
44067             callback : function(){
44068                 this.el.setStyle("z-index", "");
44069                 this.afterSlide();
44070                 if(this.split){
44071                     this.split.el.show();
44072                 }
44073                 this.fireEvent("invalidated", this);
44074                 this.fireEvent("expanded", this);
44075             },
44076             scope: this,
44077             block: true
44078         });
44079     },
44080
44081     anchors : {
44082         "west" : "left",
44083         "east" : "right",
44084         "north" : "top",
44085         "south" : "bottom"
44086     },
44087
44088     sanchors : {
44089         "west" : "l",
44090         "east" : "r",
44091         "north" : "t",
44092         "south" : "b"
44093     },
44094
44095     canchors : {
44096         "west" : "tl-tr",
44097         "east" : "tr-tl",
44098         "north" : "tl-bl",
44099         "south" : "bl-tl"
44100     },
44101
44102     getAnchor : function(){
44103         return this.anchors[this.position];
44104     },
44105
44106     getCollapseAnchor : function(){
44107         return this.canchors[this.position];
44108     },
44109
44110     getSlideAnchor : function(){
44111         return this.sanchors[this.position];
44112     },
44113
44114     getAlignAdj : function(){
44115         var cm = this.cmargins;
44116         switch(this.position){
44117             case "west":
44118                 return [0, 0];
44119             break;
44120             case "east":
44121                 return [0, 0];
44122             break;
44123             case "north":
44124                 return [0, 0];
44125             break;
44126             case "south":
44127                 return [0, 0];
44128             break;
44129         }
44130     },
44131
44132     getExpandAdj : function(){
44133         var c = this.collapsedEl, cm = this.cmargins;
44134         switch(this.position){
44135             case "west":
44136                 return [-(cm.right+c.getWidth()+cm.left), 0];
44137             break;
44138             case "east":
44139                 return [cm.right+c.getWidth()+cm.left, 0];
44140             break;
44141             case "north":
44142                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44143             break;
44144             case "south":
44145                 return [0, cm.top+cm.bottom+c.getHeight()];
44146             break;
44147         }
44148     }
44149 });/*
44150  * Based on:
44151  * Ext JS Library 1.1.1
44152  * Copyright(c) 2006-2007, Ext JS, LLC.
44153  *
44154  * Originally Released Under LGPL - original licence link has changed is not relivant.
44155  *
44156  * Fork - LGPL
44157  * <script type="text/javascript">
44158  */
44159 /*
44160  * These classes are private internal classes
44161  */
44162 Roo.bootstrap.layout.Center = function(config){
44163     config.region = "center";
44164     Roo.bootstrap.layout.Region.call(this, config);
44165     this.visible = true;
44166     this.minWidth = config.minWidth || 20;
44167     this.minHeight = config.minHeight || 20;
44168 };
44169
44170 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44171     hide : function(){
44172         // center panel can't be hidden
44173     },
44174     
44175     show : function(){
44176         // center panel can't be hidden
44177     },
44178     
44179     getMinWidth: function(){
44180         return this.minWidth;
44181     },
44182     
44183     getMinHeight: function(){
44184         return this.minHeight;
44185     }
44186 });
44187
44188
44189
44190
44191  
44192
44193
44194
44195
44196
44197
44198 Roo.bootstrap.layout.North = function(config)
44199 {
44200     config.region = 'north';
44201     config.cursor = 'n-resize';
44202     
44203     Roo.bootstrap.layout.Split.call(this, config);
44204     
44205     
44206     if(this.split){
44207         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44208         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44209         this.split.el.addClass("roo-layout-split-v");
44210     }
44211     //var size = config.initialSize || config.height;
44212     //if(this.el && typeof size != "undefined"){
44213     //    this.el.setHeight(size);
44214     //}
44215 };
44216 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44217 {
44218     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44219      
44220      
44221     onRender : function(ctr, pos)
44222     {
44223         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44224         var size = this.config.initialSize || this.config.height;
44225         if(this.el && typeof size != "undefined"){
44226             this.el.setHeight(size);
44227         }
44228     
44229     },
44230     
44231     getBox : function(){
44232         if(this.collapsed){
44233             return this.collapsedEl.getBox();
44234         }
44235         var box = this.el.getBox();
44236         if(this.split){
44237             box.height += this.split.el.getHeight();
44238         }
44239         return box;
44240     },
44241     
44242     updateBox : function(box){
44243         if(this.split && !this.collapsed){
44244             box.height -= this.split.el.getHeight();
44245             this.split.el.setLeft(box.x);
44246             this.split.el.setTop(box.y+box.height);
44247             this.split.el.setWidth(box.width);
44248         }
44249         if(this.collapsed){
44250             this.updateBody(box.width, null);
44251         }
44252         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44253     }
44254 });
44255
44256
44257
44258
44259
44260 Roo.bootstrap.layout.South = function(config){
44261     config.region = 'south';
44262     config.cursor = 's-resize';
44263     Roo.bootstrap.layout.Split.call(this, config);
44264     if(this.split){
44265         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44266         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44267         this.split.el.addClass("roo-layout-split-v");
44268     }
44269     
44270 };
44271
44272 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44273     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44274     
44275     onRender : function(ctr, pos)
44276     {
44277         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44278         var size = this.config.initialSize || this.config.height;
44279         if(this.el && typeof size != "undefined"){
44280             this.el.setHeight(size);
44281         }
44282     
44283     },
44284     
44285     getBox : function(){
44286         if(this.collapsed){
44287             return this.collapsedEl.getBox();
44288         }
44289         var box = this.el.getBox();
44290         if(this.split){
44291             var sh = this.split.el.getHeight();
44292             box.height += sh;
44293             box.y -= sh;
44294         }
44295         return box;
44296     },
44297     
44298     updateBox : function(box){
44299         if(this.split && !this.collapsed){
44300             var sh = this.split.el.getHeight();
44301             box.height -= sh;
44302             box.y += sh;
44303             this.split.el.setLeft(box.x);
44304             this.split.el.setTop(box.y-sh);
44305             this.split.el.setWidth(box.width);
44306         }
44307         if(this.collapsed){
44308             this.updateBody(box.width, null);
44309         }
44310         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44311     }
44312 });
44313
44314 Roo.bootstrap.layout.East = function(config){
44315     config.region = "east";
44316     config.cursor = "e-resize";
44317     Roo.bootstrap.layout.Split.call(this, config);
44318     if(this.split){
44319         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44320         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44321         this.split.el.addClass("roo-layout-split-h");
44322     }
44323     
44324 };
44325 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44326     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44327     
44328     onRender : function(ctr, pos)
44329     {
44330         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44331         var size = this.config.initialSize || this.config.width;
44332         if(this.el && typeof size != "undefined"){
44333             this.el.setWidth(size);
44334         }
44335     
44336     },
44337     
44338     getBox : function(){
44339         if(this.collapsed){
44340             return this.collapsedEl.getBox();
44341         }
44342         var box = this.el.getBox();
44343         if(this.split){
44344             var sw = this.split.el.getWidth();
44345             box.width += sw;
44346             box.x -= sw;
44347         }
44348         return box;
44349     },
44350
44351     updateBox : function(box){
44352         if(this.split && !this.collapsed){
44353             var sw = this.split.el.getWidth();
44354             box.width -= sw;
44355             this.split.el.setLeft(box.x);
44356             this.split.el.setTop(box.y);
44357             this.split.el.setHeight(box.height);
44358             box.x += sw;
44359         }
44360         if(this.collapsed){
44361             this.updateBody(null, box.height);
44362         }
44363         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44364     }
44365 });
44366
44367 Roo.bootstrap.layout.West = function(config){
44368     config.region = "west";
44369     config.cursor = "w-resize";
44370     
44371     Roo.bootstrap.layout.Split.call(this, config);
44372     if(this.split){
44373         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44374         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44375         this.split.el.addClass("roo-layout-split-h");
44376     }
44377     
44378 };
44379 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44380     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44381     
44382     onRender: function(ctr, pos)
44383     {
44384         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44385         var size = this.config.initialSize || this.config.width;
44386         if(typeof size != "undefined"){
44387             this.el.setWidth(size);
44388         }
44389     },
44390     
44391     getBox : function(){
44392         if(this.collapsed){
44393             return this.collapsedEl.getBox();
44394         }
44395         var box = this.el.getBox();
44396         if (box.width == 0) {
44397             box.width = this.config.width; // kludge?
44398         }
44399         if(this.split){
44400             box.width += this.split.el.getWidth();
44401         }
44402         return box;
44403     },
44404     
44405     updateBox : function(box){
44406         if(this.split && !this.collapsed){
44407             var sw = this.split.el.getWidth();
44408             box.width -= sw;
44409             this.split.el.setLeft(box.x+box.width);
44410             this.split.el.setTop(box.y);
44411             this.split.el.setHeight(box.height);
44412         }
44413         if(this.collapsed){
44414             this.updateBody(null, box.height);
44415         }
44416         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44417     }
44418 });/*
44419  * Based on:
44420  * Ext JS Library 1.1.1
44421  * Copyright(c) 2006-2007, Ext JS, LLC.
44422  *
44423  * Originally Released Under LGPL - original licence link has changed is not relivant.
44424  *
44425  * Fork - LGPL
44426  * <script type="text/javascript">
44427  */
44428 /**
44429  * @class Roo.bootstrap.paenl.Content
44430  * @extends Roo.util.Observable
44431  * @children Roo.bootstrap.Component
44432  * @parent builder Roo.bootstrap.layout.Border
44433  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44434  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44435  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44436  * @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
44437  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44438  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44439  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44440  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44441  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44442  * @cfg {String} title          The title for this panel
44443  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44444  * @cfg {String} url            Calls {@link #setUrl} with this value
44445  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44446  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44447  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44448  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44449  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44450  * @cfg {Boolean} badges render the badges
44451  * @cfg {String} cls  extra classes to use  
44452  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44453  
44454  * @constructor
44455  * Create a new ContentPanel.
44456  * @param {String/Object} config A string to set only the title or a config object
44457  
44458  */
44459 Roo.bootstrap.panel.Content = function( config){
44460     
44461     this.tpl = config.tpl || false;
44462     
44463     var el = config.el;
44464     var content = config.content;
44465
44466     if(config.autoCreate){ // xtype is available if this is called from factory
44467         el = Roo.id();
44468     }
44469     this.el = Roo.get(el);
44470     if(!this.el && config && config.autoCreate){
44471         if(typeof config.autoCreate == "object"){
44472             if(!config.autoCreate.id){
44473                 config.autoCreate.id = config.id||el;
44474             }
44475             this.el = Roo.DomHelper.append(document.body,
44476                         config.autoCreate, true);
44477         }else{
44478             var elcfg =  {
44479                 tag: "div",
44480                 cls: (config.cls || '') +
44481                     (config.background ? ' bg-' + config.background : '') +
44482                     " roo-layout-inactive-content",
44483                 id: config.id||el
44484             };
44485             if (config.iframe) {
44486                 elcfg.cn = [
44487                     {
44488                         tag : 'iframe',
44489                         style : 'border: 0px',
44490                         src : 'about:blank'
44491                     }
44492                 ];
44493             }
44494               
44495             if (config.html) {
44496                 elcfg.html = config.html;
44497                 
44498             }
44499                         
44500             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44501             if (config.iframe) {
44502                 this.iframeEl = this.el.select('iframe',true).first();
44503             }
44504             
44505         }
44506     } 
44507     this.closable = false;
44508     this.loaded = false;
44509     this.active = false;
44510    
44511       
44512     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44513         
44514         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44515         
44516         this.wrapEl = this.el; //this.el.wrap();
44517         var ti = [];
44518         if (config.toolbar.items) {
44519             ti = config.toolbar.items ;
44520             delete config.toolbar.items ;
44521         }
44522         
44523         var nitems = [];
44524         this.toolbar.render(this.wrapEl, 'before');
44525         for(var i =0;i < ti.length;i++) {
44526           //  Roo.log(['add child', items[i]]);
44527             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44528         }
44529         this.toolbar.items = nitems;
44530         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44531         delete config.toolbar;
44532         
44533     }
44534     /*
44535     // xtype created footer. - not sure if will work as we normally have to render first..
44536     if (this.footer && !this.footer.el && this.footer.xtype) {
44537         if (!this.wrapEl) {
44538             this.wrapEl = this.el.wrap();
44539         }
44540     
44541         this.footer.container = this.wrapEl.createChild();
44542          
44543         this.footer = Roo.factory(this.footer, Roo);
44544         
44545     }
44546     */
44547     
44548      if(typeof config == "string"){
44549         this.title = config;
44550     }else{
44551         Roo.apply(this, config);
44552     }
44553     
44554     if(this.resizeEl){
44555         this.resizeEl = Roo.get(this.resizeEl, true);
44556     }else{
44557         this.resizeEl = this.el;
44558     }
44559     // handle view.xtype
44560     
44561  
44562     
44563     
44564     this.addEvents({
44565         /**
44566          * @event activate
44567          * Fires when this panel is activated. 
44568          * @param {Roo.ContentPanel} this
44569          */
44570         "activate" : true,
44571         /**
44572          * @event deactivate
44573          * Fires when this panel is activated. 
44574          * @param {Roo.ContentPanel} this
44575          */
44576         "deactivate" : true,
44577
44578         /**
44579          * @event resize
44580          * Fires when this panel is resized if fitToFrame is true.
44581          * @param {Roo.ContentPanel} this
44582          * @param {Number} width The width after any component adjustments
44583          * @param {Number} height The height after any component adjustments
44584          */
44585         "resize" : true,
44586         
44587          /**
44588          * @event render
44589          * Fires when this tab is created
44590          * @param {Roo.ContentPanel} this
44591          */
44592         "render" : true,
44593         
44594           /**
44595          * @event scroll
44596          * Fires when this content is scrolled
44597          * @param {Roo.ContentPanel} this
44598          * @param {Event} scrollEvent
44599          */
44600         "scroll" : true
44601         
44602         
44603         
44604     });
44605     
44606
44607     
44608     
44609     if(this.autoScroll && !this.iframe){
44610         this.resizeEl.setStyle("overflow", "auto");
44611         this.resizeEl.on('scroll', this.onScroll, this);
44612     } else {
44613         // fix randome scrolling
44614         //this.el.on('scroll', function() {
44615         //    Roo.log('fix random scolling');
44616         //    this.scrollTo('top',0); 
44617         //});
44618     }
44619     content = content || this.content;
44620     if(content){
44621         this.setContent(content);
44622     }
44623     if(config && config.url){
44624         this.setUrl(this.url, this.params, this.loadOnce);
44625     }
44626     
44627     
44628     
44629     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44630     
44631     if (this.view && typeof(this.view.xtype) != 'undefined') {
44632         this.view.el = this.el.appendChild(document.createElement("div"));
44633         this.view = Roo.factory(this.view); 
44634         this.view.render  &&  this.view.render(false, '');  
44635     }
44636     
44637     
44638     this.fireEvent('render', this);
44639 };
44640
44641 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44642     
44643     cls : '',
44644     background : '',
44645     
44646     tabTip : '',
44647     
44648     iframe : false,
44649     iframeEl : false,
44650     
44651     /* Resize Element - use this to work out scroll etc. */
44652     resizeEl : false,
44653     
44654     setRegion : function(region){
44655         this.region = region;
44656         this.setActiveClass(region && !this.background);
44657     },
44658     
44659     
44660     setActiveClass: function(state)
44661     {
44662         if(state){
44663            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44664            this.el.setStyle('position','relative');
44665         }else{
44666            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44667            this.el.setStyle('position', 'absolute');
44668         } 
44669     },
44670     
44671     /**
44672      * Returns the toolbar for this Panel if one was configured. 
44673      * @return {Roo.Toolbar} 
44674      */
44675     getToolbar : function(){
44676         return this.toolbar;
44677     },
44678     
44679     setActiveState : function(active)
44680     {
44681         this.active = active;
44682         this.setActiveClass(active);
44683         if(!active){
44684             if(this.fireEvent("deactivate", this) === false){
44685                 return false;
44686             }
44687             return true;
44688         }
44689         this.fireEvent("activate", this);
44690         return true;
44691     },
44692     /**
44693      * Updates this panel's element (not for iframe)
44694      * @param {String} content The new content
44695      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44696     */
44697     setContent : function(content, loadScripts){
44698         if (this.iframe) {
44699             return;
44700         }
44701         
44702         this.el.update(content, loadScripts);
44703     },
44704
44705     ignoreResize : function(w, h)
44706     {
44707         //return false; // always resize?
44708         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44709             return true;
44710         }else{
44711             this.lastSize = {width: w, height: h};
44712             return false;
44713         }
44714     },
44715     /**
44716      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44717      * @return {Roo.UpdateManager} The UpdateManager
44718      */
44719     getUpdateManager : function(){
44720         if (this.iframe) {
44721             return false;
44722         }
44723         return this.el.getUpdateManager();
44724     },
44725      /**
44726      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44727      * Does not work with IFRAME contents
44728      * @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:
44729 <pre><code>
44730 panel.load({
44731     url: "your-url.php",
44732     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44733     callback: yourFunction,
44734     scope: yourObject, //(optional scope)
44735     discardUrl: false,
44736     nocache: false,
44737     text: "Loading...",
44738     timeout: 30,
44739     scripts: false
44740 });
44741 </code></pre>
44742      
44743      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44744      * 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.
44745      * @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}
44746      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44747      * @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.
44748      * @return {Roo.ContentPanel} this
44749      */
44750     load : function(){
44751         
44752         if (this.iframe) {
44753             return this;
44754         }
44755         
44756         var um = this.el.getUpdateManager();
44757         um.update.apply(um, arguments);
44758         return this;
44759     },
44760
44761
44762     /**
44763      * 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.
44764      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44765      * @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)
44766      * @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)
44767      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44768      */
44769     setUrl : function(url, params, loadOnce){
44770         if (this.iframe) {
44771             this.iframeEl.dom.src = url;
44772             return false;
44773         }
44774         
44775         if(this.refreshDelegate){
44776             this.removeListener("activate", this.refreshDelegate);
44777         }
44778         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44779         this.on("activate", this.refreshDelegate);
44780         return this.el.getUpdateManager();
44781     },
44782     
44783     _handleRefresh : function(url, params, loadOnce){
44784         if(!loadOnce || !this.loaded){
44785             var updater = this.el.getUpdateManager();
44786             updater.update(url, params, this._setLoaded.createDelegate(this));
44787         }
44788     },
44789     
44790     _setLoaded : function(){
44791         this.loaded = true;
44792     }, 
44793     
44794     /**
44795      * Returns this panel's id
44796      * @return {String} 
44797      */
44798     getId : function(){
44799         return this.el.id;
44800     },
44801     
44802     /** 
44803      * Returns this panel's element - used by regiosn to add.
44804      * @return {Roo.Element} 
44805      */
44806     getEl : function(){
44807         return this.wrapEl || this.el;
44808     },
44809     
44810    
44811     
44812     adjustForComponents : function(width, height)
44813     {
44814         //Roo.log('adjustForComponents ');
44815         if(this.resizeEl != this.el){
44816             width -= this.el.getFrameWidth('lr');
44817             height -= this.el.getFrameWidth('tb');
44818         }
44819         if(this.toolbar){
44820             var te = this.toolbar.getEl();
44821             te.setWidth(width);
44822             height -= te.getHeight();
44823         }
44824         if(this.footer){
44825             var te = this.footer.getEl();
44826             te.setWidth(width);
44827             height -= te.getHeight();
44828         }
44829         
44830         
44831         if(this.adjustments){
44832             width += this.adjustments[0];
44833             height += this.adjustments[1];
44834         }
44835         return {"width": width, "height": height};
44836     },
44837     
44838     setSize : function(width, height){
44839         if(this.fitToFrame && !this.ignoreResize(width, height)){
44840             if(this.fitContainer && this.resizeEl != this.el){
44841                 this.el.setSize(width, height);
44842             }
44843             var size = this.adjustForComponents(width, height);
44844             if (this.iframe) {
44845                 this.iframeEl.setSize(width,height);
44846             }
44847             
44848             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44849             this.fireEvent('resize', this, size.width, size.height);
44850             
44851             
44852         }
44853     },
44854     
44855     /**
44856      * Returns this panel's title
44857      * @return {String} 
44858      */
44859     getTitle : function(){
44860         
44861         if (typeof(this.title) != 'object') {
44862             return this.title;
44863         }
44864         
44865         var t = '';
44866         for (var k in this.title) {
44867             if (!this.title.hasOwnProperty(k)) {
44868                 continue;
44869             }
44870             
44871             if (k.indexOf('-') >= 0) {
44872                 var s = k.split('-');
44873                 for (var i = 0; i<s.length; i++) {
44874                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44875                 }
44876             } else {
44877                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44878             }
44879         }
44880         return t;
44881     },
44882     
44883     /**
44884      * Set this panel's title
44885      * @param {String} title
44886      */
44887     setTitle : function(title){
44888         this.title = title;
44889         if(this.region){
44890             this.region.updatePanelTitle(this, title);
44891         }
44892     },
44893     
44894     /**
44895      * Returns true is this panel was configured to be closable
44896      * @return {Boolean} 
44897      */
44898     isClosable : function(){
44899         return this.closable;
44900     },
44901     
44902     beforeSlide : function(){
44903         this.el.clip();
44904         this.resizeEl.clip();
44905     },
44906     
44907     afterSlide : function(){
44908         this.el.unclip();
44909         this.resizeEl.unclip();
44910     },
44911     
44912     /**
44913      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44914      *   Will fail silently if the {@link #setUrl} method has not been called.
44915      *   This does not activate the panel, just updates its content.
44916      */
44917     refresh : function(){
44918         if(this.refreshDelegate){
44919            this.loaded = false;
44920            this.refreshDelegate();
44921         }
44922     },
44923     
44924     /**
44925      * Destroys this panel
44926      */
44927     destroy : function(){
44928         this.el.removeAllListeners();
44929         var tempEl = document.createElement("span");
44930         tempEl.appendChild(this.el.dom);
44931         tempEl.innerHTML = "";
44932         this.el.remove();
44933         this.el = null;
44934     },
44935     
44936     /**
44937      * form - if the content panel contains a form - this is a reference to it.
44938      * @type {Roo.form.Form}
44939      */
44940     form : false,
44941     /**
44942      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44943      *    This contains a reference to it.
44944      * @type {Roo.View}
44945      */
44946     view : false,
44947     
44948       /**
44949      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44950      * <pre><code>
44951
44952 layout.addxtype({
44953        xtype : 'Form',
44954        items: [ .... ]
44955    }
44956 );
44957
44958 </code></pre>
44959      * @param {Object} cfg Xtype definition of item to add.
44960      */
44961     
44962     
44963     getChildContainer: function () {
44964         return this.getEl();
44965     },
44966     
44967     
44968     onScroll : function(e)
44969     {
44970         this.fireEvent('scroll', this, e);
44971     }
44972     
44973     
44974     /*
44975         var  ret = new Roo.factory(cfg);
44976         return ret;
44977         
44978         
44979         // add form..
44980         if (cfg.xtype.match(/^Form$/)) {
44981             
44982             var el;
44983             //if (this.footer) {
44984             //    el = this.footer.container.insertSibling(false, 'before');
44985             //} else {
44986                 el = this.el.createChild();
44987             //}
44988
44989             this.form = new  Roo.form.Form(cfg);
44990             
44991             
44992             if ( this.form.allItems.length) {
44993                 this.form.render(el.dom);
44994             }
44995             return this.form;
44996         }
44997         // should only have one of theses..
44998         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44999             // views.. should not be just added - used named prop 'view''
45000             
45001             cfg.el = this.el.appendChild(document.createElement("div"));
45002             // factory?
45003             
45004             var ret = new Roo.factory(cfg);
45005              
45006              ret.render && ret.render(false, ''); // render blank..
45007             this.view = ret;
45008             return ret;
45009         }
45010         return false;
45011     }
45012     \*/
45013 });
45014  
45015 /**
45016  * @class Roo.bootstrap.panel.Grid
45017  * @extends Roo.bootstrap.panel.Content
45018  * @constructor
45019  * Create a new GridPanel.
45020  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45021  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45022  * @param {Object} config A the config object
45023   
45024  */
45025
45026
45027
45028 Roo.bootstrap.panel.Grid = function(config)
45029 {
45030     
45031       
45032     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45033         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45034
45035     config.el = this.wrapper;
45036     //this.el = this.wrapper;
45037     
45038       if (config.container) {
45039         // ctor'ed from a Border/panel.grid
45040         
45041         
45042         this.wrapper.setStyle("overflow", "hidden");
45043         this.wrapper.addClass('roo-grid-container');
45044
45045     }
45046     
45047     
45048     if(config.toolbar){
45049         var tool_el = this.wrapper.createChild();    
45050         this.toolbar = Roo.factory(config.toolbar);
45051         var ti = [];
45052         if (config.toolbar.items) {
45053             ti = config.toolbar.items ;
45054             delete config.toolbar.items ;
45055         }
45056         
45057         var nitems = [];
45058         this.toolbar.render(tool_el);
45059         for(var i =0;i < ti.length;i++) {
45060           //  Roo.log(['add child', items[i]]);
45061             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45062         }
45063         this.toolbar.items = nitems;
45064         
45065         delete config.toolbar;
45066     }
45067     
45068     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45069     config.grid.scrollBody = true;;
45070     config.grid.monitorWindowResize = false; // turn off autosizing
45071     config.grid.autoHeight = false;
45072     config.grid.autoWidth = false;
45073     
45074     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45075     
45076     if (config.background) {
45077         // render grid on panel activation (if panel background)
45078         this.on('activate', function(gp) {
45079             if (!gp.grid.rendered) {
45080                 gp.grid.render(this.wrapper);
45081                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45082             }
45083         });
45084             
45085     } else {
45086         this.grid.render(this.wrapper);
45087         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45088
45089     }
45090     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45091     // ??? needed ??? config.el = this.wrapper;
45092     
45093     
45094     
45095   
45096     // xtype created footer. - not sure if will work as we normally have to render first..
45097     if (this.footer && !this.footer.el && this.footer.xtype) {
45098         
45099         var ctr = this.grid.getView().getFooterPanel(true);
45100         this.footer.dataSource = this.grid.dataSource;
45101         this.footer = Roo.factory(this.footer, Roo);
45102         this.footer.render(ctr);
45103         
45104     }
45105     
45106     
45107     
45108     
45109      
45110 };
45111
45112 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45113 {
45114   
45115     getId : function(){
45116         return this.grid.id;
45117     },
45118     
45119     /**
45120      * Returns the grid for this panel
45121      * @return {Roo.bootstrap.Table} 
45122      */
45123     getGrid : function(){
45124         return this.grid;    
45125     },
45126     
45127     setSize : function(width, height)
45128     {
45129      
45130         //if(!this.ignoreResize(width, height)){
45131             var grid = this.grid;
45132             var size = this.adjustForComponents(width, height);
45133             // tfoot is not a footer?
45134           
45135             
45136             var gridel = grid.getGridEl();
45137             gridel.setSize(size.width, size.height);
45138             
45139             var tbd = grid.getGridEl().select('tbody', true).first();
45140             var thd = grid.getGridEl().select('thead',true).first();
45141             var tbf= grid.getGridEl().select('tfoot', true).first();
45142
45143             if (tbf) {
45144                 size.height -= tbf.getHeight();
45145             }
45146             if (thd) {
45147                 size.height -= thd.getHeight();
45148             }
45149             
45150             tbd.setSize(size.width, size.height );
45151             // this is for the account management tab -seems to work there.
45152             var thd = grid.getGridEl().select('thead',true).first();
45153             //if (tbd) {
45154             //    tbd.setSize(size.width, size.height - thd.getHeight());
45155             //}
45156              
45157             grid.autoSize();
45158         //}
45159    
45160     },
45161      
45162     
45163     
45164     beforeSlide : function(){
45165         this.grid.getView().scroller.clip();
45166     },
45167     
45168     afterSlide : function(){
45169         this.grid.getView().scroller.unclip();
45170     },
45171     
45172     destroy : function(){
45173         this.grid.destroy();
45174         delete this.grid;
45175         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45176     }
45177 });
45178
45179 /**
45180  * @class Roo.bootstrap.panel.Nest
45181  * @extends Roo.bootstrap.panel.Content
45182  * @constructor
45183  * Create a new Panel, that can contain a layout.Border.
45184  * 
45185  * 
45186  * @param {String/Object} config A string to set only the title or a config object
45187  */
45188 Roo.bootstrap.panel.Nest = function(config)
45189 {
45190     // construct with only one argument..
45191     /* FIXME - implement nicer consturctors
45192     if (layout.layout) {
45193         config = layout;
45194         layout = config.layout;
45195         delete config.layout;
45196     }
45197     if (layout.xtype && !layout.getEl) {
45198         // then layout needs constructing..
45199         layout = Roo.factory(layout, Roo);
45200     }
45201     */
45202     
45203     config.el =  config.layout.getEl();
45204     
45205     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45206     
45207     config.layout.monitorWindowResize = false; // turn off autosizing
45208     this.layout = config.layout;
45209     this.layout.getEl().addClass("roo-layout-nested-layout");
45210     this.layout.parent = this;
45211     
45212     
45213     
45214     
45215 };
45216
45217 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45218     /**
45219     * @cfg {Roo.BorderLayout} layout The layout for this panel
45220     */
45221     layout : false,
45222
45223     setSize : function(width, height){
45224         if(!this.ignoreResize(width, height)){
45225             var size = this.adjustForComponents(width, height);
45226             var el = this.layout.getEl();
45227             if (size.height < 1) {
45228                 el.setWidth(size.width);   
45229             } else {
45230                 el.setSize(size.width, size.height);
45231             }
45232             var touch = el.dom.offsetWidth;
45233             this.layout.layout();
45234             // ie requires a double layout on the first pass
45235             if(Roo.isIE && !this.initialized){
45236                 this.initialized = true;
45237                 this.layout.layout();
45238             }
45239         }
45240     },
45241     
45242     // activate all subpanels if not currently active..
45243     
45244     setActiveState : function(active){
45245         this.active = active;
45246         this.setActiveClass(active);
45247         
45248         if(!active){
45249             this.fireEvent("deactivate", this);
45250             return;
45251         }
45252         
45253         this.fireEvent("activate", this);
45254         // not sure if this should happen before or after..
45255         if (!this.layout) {
45256             return; // should not happen..
45257         }
45258         var reg = false;
45259         for (var r in this.layout.regions) {
45260             reg = this.layout.getRegion(r);
45261             if (reg.getActivePanel()) {
45262                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45263                 reg.setActivePanel(reg.getActivePanel());
45264                 continue;
45265             }
45266             if (!reg.panels.length) {
45267                 continue;
45268             }
45269             reg.showPanel(reg.getPanel(0));
45270         }
45271         
45272         
45273         
45274         
45275     },
45276     
45277     /**
45278      * Returns the nested BorderLayout for this panel
45279      * @return {Roo.BorderLayout} 
45280      */
45281     getLayout : function(){
45282         return this.layout;
45283     },
45284     
45285      /**
45286      * Adds a xtype elements to the layout of the nested panel
45287      * <pre><code>
45288
45289 panel.addxtype({
45290        xtype : 'ContentPanel',
45291        region: 'west',
45292        items: [ .... ]
45293    }
45294 );
45295
45296 panel.addxtype({
45297         xtype : 'NestedLayoutPanel',
45298         region: 'west',
45299         layout: {
45300            center: { },
45301            west: { }   
45302         },
45303         items : [ ... list of content panels or nested layout panels.. ]
45304    }
45305 );
45306 </code></pre>
45307      * @param {Object} cfg Xtype definition of item to add.
45308      */
45309     addxtype : function(cfg) {
45310         return this.layout.addxtype(cfg);
45311     
45312     }
45313 });/*
45314  * Based on:
45315  * Ext JS Library 1.1.1
45316  * Copyright(c) 2006-2007, Ext JS, LLC.
45317  *
45318  * Originally Released Under LGPL - original licence link has changed is not relivant.
45319  *
45320  * Fork - LGPL
45321  * <script type="text/javascript">
45322  */
45323 /**
45324  * @class Roo.TabPanel
45325  * @extends Roo.util.Observable
45326  * A lightweight tab container.
45327  * <br><br>
45328  * Usage:
45329  * <pre><code>
45330 // basic tabs 1, built from existing content
45331 var tabs = new Roo.TabPanel("tabs1");
45332 tabs.addTab("script", "View Script");
45333 tabs.addTab("markup", "View Markup");
45334 tabs.activate("script");
45335
45336 // more advanced tabs, built from javascript
45337 var jtabs = new Roo.TabPanel("jtabs");
45338 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45339
45340 // set up the UpdateManager
45341 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45342 var updater = tab2.getUpdateManager();
45343 updater.setDefaultUrl("ajax1.htm");
45344 tab2.on('activate', updater.refresh, updater, true);
45345
45346 // Use setUrl for Ajax loading
45347 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45348 tab3.setUrl("ajax2.htm", null, true);
45349
45350 // Disabled tab
45351 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45352 tab4.disable();
45353
45354 jtabs.activate("jtabs-1");
45355  * </code></pre>
45356  * @constructor
45357  * Create a new TabPanel.
45358  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45359  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45360  */
45361 Roo.bootstrap.panel.Tabs = function(config){
45362     /**
45363     * The container element for this TabPanel.
45364     * @type Roo.Element
45365     */
45366     this.el = Roo.get(config.el);
45367     delete config.el;
45368     if(config){
45369         if(typeof config == "boolean"){
45370             this.tabPosition = config ? "bottom" : "top";
45371         }else{
45372             Roo.apply(this, config);
45373         }
45374     }
45375     
45376     if(this.tabPosition == "bottom"){
45377         // if tabs are at the bottom = create the body first.
45378         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45379         this.el.addClass("roo-tabs-bottom");
45380     }
45381     // next create the tabs holders
45382     
45383     if (this.tabPosition == "west"){
45384         
45385         var reg = this.region; // fake it..
45386         while (reg) {
45387             if (!reg.mgr.parent) {
45388                 break;
45389             }
45390             reg = reg.mgr.parent.region;
45391         }
45392         Roo.log("got nest?");
45393         Roo.log(reg);
45394         if (reg.mgr.getRegion('west')) {
45395             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45396             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45397             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45398             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45399             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45400         
45401             
45402         }
45403         
45404         
45405     } else {
45406      
45407         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45408         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45409         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45410         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45411     }
45412     
45413     
45414     if(Roo.isIE){
45415         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45416     }
45417     
45418     // finally - if tabs are at the top, then create the body last..
45419     if(this.tabPosition != "bottom"){
45420         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45421          * @type Roo.Element
45422          */
45423         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45424         this.el.addClass("roo-tabs-top");
45425     }
45426     this.items = [];
45427
45428     this.bodyEl.setStyle("position", "relative");
45429
45430     this.active = null;
45431     this.activateDelegate = this.activate.createDelegate(this);
45432
45433     this.addEvents({
45434         /**
45435          * @event tabchange
45436          * Fires when the active tab changes
45437          * @param {Roo.TabPanel} this
45438          * @param {Roo.TabPanelItem} activePanel The new active tab
45439          */
45440         "tabchange": true,
45441         /**
45442          * @event beforetabchange
45443          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45444          * @param {Roo.TabPanel} this
45445          * @param {Object} e Set cancel to true on this object to cancel the tab change
45446          * @param {Roo.TabPanelItem} tab The tab being changed to
45447          */
45448         "beforetabchange" : true
45449     });
45450
45451     Roo.EventManager.onWindowResize(this.onResize, this);
45452     this.cpad = this.el.getPadding("lr");
45453     this.hiddenCount = 0;
45454
45455
45456     // toolbar on the tabbar support...
45457     if (this.toolbar) {
45458         alert("no toolbar support yet");
45459         this.toolbar  = false;
45460         /*
45461         var tcfg = this.toolbar;
45462         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45463         this.toolbar = new Roo.Toolbar(tcfg);
45464         if (Roo.isSafari) {
45465             var tbl = tcfg.container.child('table', true);
45466             tbl.setAttribute('width', '100%');
45467         }
45468         */
45469         
45470     }
45471    
45472
45473
45474     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45475 };
45476
45477 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45478     /*
45479      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45480      */
45481     tabPosition : "top",
45482     /*
45483      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45484      */
45485     currentTabWidth : 0,
45486     /*
45487      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45488      */
45489     minTabWidth : 40,
45490     /*
45491      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45492      */
45493     maxTabWidth : 250,
45494     /*
45495      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45496      */
45497     preferredTabWidth : 175,
45498     /*
45499      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45500      */
45501     resizeTabs : false,
45502     /*
45503      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45504      */
45505     monitorResize : true,
45506     /*
45507      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45508      */
45509     toolbar : false,  // set by caller..
45510     
45511     region : false, /// set by caller
45512     
45513     disableTooltips : true, // not used yet...
45514
45515     /**
45516      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45517      * @param {String} id The id of the div to use <b>or create</b>
45518      * @param {String} text The text for the tab
45519      * @param {String} content (optional) Content to put in the TabPanelItem body
45520      * @param {Boolean} closable (optional) True to create a close icon on the tab
45521      * @return {Roo.TabPanelItem} The created TabPanelItem
45522      */
45523     addTab : function(id, text, content, closable, tpl)
45524     {
45525         var item = new Roo.bootstrap.panel.TabItem({
45526             panel: this,
45527             id : id,
45528             text : text,
45529             closable : closable,
45530             tpl : tpl
45531         });
45532         this.addTabItem(item);
45533         if(content){
45534             item.setContent(content);
45535         }
45536         return item;
45537     },
45538
45539     /**
45540      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45541      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45542      * @return {Roo.TabPanelItem}
45543      */
45544     getTab : function(id){
45545         return this.items[id];
45546     },
45547
45548     /**
45549      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45550      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45551      */
45552     hideTab : function(id){
45553         var t = this.items[id];
45554         if(!t.isHidden()){
45555            t.setHidden(true);
45556            this.hiddenCount++;
45557            this.autoSizeTabs();
45558         }
45559     },
45560
45561     /**
45562      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45563      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45564      */
45565     unhideTab : function(id){
45566         var t = this.items[id];
45567         if(t.isHidden()){
45568            t.setHidden(false);
45569            this.hiddenCount--;
45570            this.autoSizeTabs();
45571         }
45572     },
45573
45574     /**
45575      * Adds an existing {@link Roo.TabPanelItem}.
45576      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45577      */
45578     addTabItem : function(item)
45579     {
45580         this.items[item.id] = item;
45581         this.items.push(item);
45582         this.autoSizeTabs();
45583       //  if(this.resizeTabs){
45584     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45585   //         this.autoSizeTabs();
45586 //        }else{
45587 //            item.autoSize();
45588        // }
45589     },
45590
45591     /**
45592      * Removes a {@link Roo.TabPanelItem}.
45593      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45594      */
45595     removeTab : function(id){
45596         var items = this.items;
45597         var tab = items[id];
45598         if(!tab) { return; }
45599         var index = items.indexOf(tab);
45600         if(this.active == tab && items.length > 1){
45601             var newTab = this.getNextAvailable(index);
45602             if(newTab) {
45603                 newTab.activate();
45604             }
45605         }
45606         this.stripEl.dom.removeChild(tab.pnode.dom);
45607         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45608             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45609         }
45610         items.splice(index, 1);
45611         delete this.items[tab.id];
45612         tab.fireEvent("close", tab);
45613         tab.purgeListeners();
45614         this.autoSizeTabs();
45615     },
45616
45617     getNextAvailable : function(start){
45618         var items = this.items;
45619         var index = start;
45620         // look for a next tab that will slide over to
45621         // replace the one being removed
45622         while(index < items.length){
45623             var item = items[++index];
45624             if(item && !item.isHidden()){
45625                 return item;
45626             }
45627         }
45628         // if one isn't found select the previous tab (on the left)
45629         index = start;
45630         while(index >= 0){
45631             var item = items[--index];
45632             if(item && !item.isHidden()){
45633                 return item;
45634             }
45635         }
45636         return null;
45637     },
45638
45639     /**
45640      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45641      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45642      */
45643     disableTab : function(id){
45644         var tab = this.items[id];
45645         if(tab && this.active != tab){
45646             tab.disable();
45647         }
45648     },
45649
45650     /**
45651      * Enables a {@link Roo.TabPanelItem} that is disabled.
45652      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45653      */
45654     enableTab : function(id){
45655         var tab = this.items[id];
45656         tab.enable();
45657     },
45658
45659     /**
45660      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45661      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45662      * @return {Roo.TabPanelItem} The TabPanelItem.
45663      */
45664     activate : function(id)
45665     {
45666         //Roo.log('activite:'  + id);
45667         
45668         var tab = this.items[id];
45669         if(!tab){
45670             return null;
45671         }
45672         if(tab == this.active || tab.disabled){
45673             return tab;
45674         }
45675         var e = {};
45676         this.fireEvent("beforetabchange", this, e, tab);
45677         if(e.cancel !== true && !tab.disabled){
45678             if(this.active){
45679                 this.active.hide();
45680             }
45681             this.active = this.items[id];
45682             this.active.show();
45683             this.fireEvent("tabchange", this, this.active);
45684         }
45685         return tab;
45686     },
45687
45688     /**
45689      * Gets the active {@link Roo.TabPanelItem}.
45690      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45691      */
45692     getActiveTab : function(){
45693         return this.active;
45694     },
45695
45696     /**
45697      * Updates the tab body element to fit the height of the container element
45698      * for overflow scrolling
45699      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45700      */
45701     syncHeight : function(targetHeight){
45702         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45703         var bm = this.bodyEl.getMargins();
45704         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45705         this.bodyEl.setHeight(newHeight);
45706         return newHeight;
45707     },
45708
45709     onResize : function(){
45710         if(this.monitorResize){
45711             this.autoSizeTabs();
45712         }
45713     },
45714
45715     /**
45716      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45717      */
45718     beginUpdate : function(){
45719         this.updating = true;
45720     },
45721
45722     /**
45723      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45724      */
45725     endUpdate : function(){
45726         this.updating = false;
45727         this.autoSizeTabs();
45728     },
45729
45730     /**
45731      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45732      */
45733     autoSizeTabs : function()
45734     {
45735         var count = this.items.length;
45736         var vcount = count - this.hiddenCount;
45737         
45738         if (vcount < 2) {
45739             this.stripEl.hide();
45740         } else {
45741             this.stripEl.show();
45742         }
45743         
45744         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45745             return;
45746         }
45747         
45748         
45749         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45750         var availWidth = Math.floor(w / vcount);
45751         var b = this.stripBody;
45752         if(b.getWidth() > w){
45753             var tabs = this.items;
45754             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45755             if(availWidth < this.minTabWidth){
45756                 /*if(!this.sleft){    // incomplete scrolling code
45757                     this.createScrollButtons();
45758                 }
45759                 this.showScroll();
45760                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45761             }
45762         }else{
45763             if(this.currentTabWidth < this.preferredTabWidth){
45764                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45765             }
45766         }
45767     },
45768
45769     /**
45770      * Returns the number of tabs in this TabPanel.
45771      * @return {Number}
45772      */
45773      getCount : function(){
45774          return this.items.length;
45775      },
45776
45777     /**
45778      * Resizes all the tabs to the passed width
45779      * @param {Number} The new width
45780      */
45781     setTabWidth : function(width){
45782         this.currentTabWidth = width;
45783         for(var i = 0, len = this.items.length; i < len; i++) {
45784                 if(!this.items[i].isHidden()) {
45785                 this.items[i].setWidth(width);
45786             }
45787         }
45788     },
45789
45790     /**
45791      * Destroys this TabPanel
45792      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45793      */
45794     destroy : function(removeEl){
45795         Roo.EventManager.removeResizeListener(this.onResize, this);
45796         for(var i = 0, len = this.items.length; i < len; i++){
45797             this.items[i].purgeListeners();
45798         }
45799         if(removeEl === true){
45800             this.el.update("");
45801             this.el.remove();
45802         }
45803     },
45804     
45805     createStrip : function(container)
45806     {
45807         var strip = document.createElement("nav");
45808         strip.className = Roo.bootstrap.version == 4 ?
45809             "navbar-light bg-light" : 
45810             "navbar navbar-default"; //"x-tabs-wrap";
45811         container.appendChild(strip);
45812         return strip;
45813     },
45814     
45815     createStripList : function(strip)
45816     {
45817         // div wrapper for retard IE
45818         // returns the "tr" element.
45819         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45820         //'<div class="x-tabs-strip-wrap">'+
45821           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45822           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45823         return strip.firstChild; //.firstChild.firstChild.firstChild;
45824     },
45825     createBody : function(container)
45826     {
45827         var body = document.createElement("div");
45828         Roo.id(body, "tab-body");
45829         //Roo.fly(body).addClass("x-tabs-body");
45830         Roo.fly(body).addClass("tab-content");
45831         container.appendChild(body);
45832         return body;
45833     },
45834     createItemBody :function(bodyEl, id){
45835         var body = Roo.getDom(id);
45836         if(!body){
45837             body = document.createElement("div");
45838             body.id = id;
45839         }
45840         //Roo.fly(body).addClass("x-tabs-item-body");
45841         Roo.fly(body).addClass("tab-pane");
45842          bodyEl.insertBefore(body, bodyEl.firstChild);
45843         return body;
45844     },
45845     /** @private */
45846     createStripElements :  function(stripEl, text, closable, tpl)
45847     {
45848         var td = document.createElement("li"); // was td..
45849         td.className = 'nav-item';
45850         
45851         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45852         
45853         
45854         stripEl.appendChild(td);
45855         /*if(closable){
45856             td.className = "x-tabs-closable";
45857             if(!this.closeTpl){
45858                 this.closeTpl = new Roo.Template(
45859                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45860                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45861                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45862                 );
45863             }
45864             var el = this.closeTpl.overwrite(td, {"text": text});
45865             var close = el.getElementsByTagName("div")[0];
45866             var inner = el.getElementsByTagName("em")[0];
45867             return {"el": el, "close": close, "inner": inner};
45868         } else {
45869         */
45870         // not sure what this is..
45871 //            if(!this.tabTpl){
45872                 //this.tabTpl = new Roo.Template(
45873                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45874                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45875                 //);
45876 //                this.tabTpl = new Roo.Template(
45877 //                   '<a href="#">' +
45878 //                   '<span unselectable="on"' +
45879 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45880 //                            ' >{text}</span></a>'
45881 //                );
45882 //                
45883 //            }
45884
45885
45886             var template = tpl || this.tabTpl || false;
45887             
45888             if(!template){
45889                 template =  new Roo.Template(
45890                         Roo.bootstrap.version == 4 ? 
45891                             (
45892                                 '<a class="nav-link" href="#" unselectable="on"' +
45893                                      (this.disableTooltips ? '' : ' title="{text}"') +
45894                                      ' >{text}</a>'
45895                             ) : (
45896                                 '<a class="nav-link" href="#">' +
45897                                 '<span unselectable="on"' +
45898                                          (this.disableTooltips ? '' : ' title="{text}"') +
45899                                     ' >{text}</span></a>'
45900                             )
45901                 );
45902             }
45903             
45904             switch (typeof(template)) {
45905                 case 'object' :
45906                     break;
45907                 case 'string' :
45908                     template = new Roo.Template(template);
45909                     break;
45910                 default :
45911                     break;
45912             }
45913             
45914             var el = template.overwrite(td, {"text": text});
45915             
45916             var inner = el.getElementsByTagName("span")[0];
45917             
45918             return {"el": el, "inner": inner};
45919             
45920     }
45921         
45922     
45923 });
45924
45925 /**
45926  * @class Roo.TabPanelItem
45927  * @extends Roo.util.Observable
45928  * Represents an individual item (tab plus body) in a TabPanel.
45929  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45930  * @param {String} id The id of this TabPanelItem
45931  * @param {String} text The text for the tab of this TabPanelItem
45932  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45933  */
45934 Roo.bootstrap.panel.TabItem = function(config){
45935     /**
45936      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45937      * @type Roo.TabPanel
45938      */
45939     this.tabPanel = config.panel;
45940     /**
45941      * The id for this TabPanelItem
45942      * @type String
45943      */
45944     this.id = config.id;
45945     /** @private */
45946     this.disabled = false;
45947     /** @private */
45948     this.text = config.text;
45949     /** @private */
45950     this.loaded = false;
45951     this.closable = config.closable;
45952
45953     /**
45954      * The body element for this TabPanelItem.
45955      * @type Roo.Element
45956      */
45957     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45958     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45959     this.bodyEl.setStyle("display", "block");
45960     this.bodyEl.setStyle("zoom", "1");
45961     //this.hideAction();
45962
45963     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45964     /** @private */
45965     this.el = Roo.get(els.el);
45966     this.inner = Roo.get(els.inner, true);
45967      this.textEl = Roo.bootstrap.version == 4 ?
45968         this.el : Roo.get(this.el.dom.firstChild, true);
45969
45970     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45971     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45972
45973     
45974 //    this.el.on("mousedown", this.onTabMouseDown, this);
45975     this.el.on("click", this.onTabClick, this);
45976     /** @private */
45977     if(config.closable){
45978         var c = Roo.get(els.close, true);
45979         c.dom.title = this.closeText;
45980         c.addClassOnOver("close-over");
45981         c.on("click", this.closeClick, this);
45982      }
45983
45984     this.addEvents({
45985          /**
45986          * @event activate
45987          * Fires when this tab becomes the active tab.
45988          * @param {Roo.TabPanel} tabPanel The parent TabPanel
45989          * @param {Roo.TabPanelItem} this
45990          */
45991         "activate": true,
45992         /**
45993          * @event beforeclose
45994          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45995          * @param {Roo.TabPanelItem} this
45996          * @param {Object} e Set cancel to true on this object to cancel the close.
45997          */
45998         "beforeclose": true,
45999         /**
46000          * @event close
46001          * Fires when this tab is closed.
46002          * @param {Roo.TabPanelItem} this
46003          */
46004          "close": true,
46005         /**
46006          * @event deactivate
46007          * Fires when this tab is no longer the active tab.
46008          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46009          * @param {Roo.TabPanelItem} this
46010          */
46011          "deactivate" : true
46012     });
46013     this.hidden = false;
46014
46015     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46016 };
46017
46018 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46019            {
46020     purgeListeners : function(){
46021        Roo.util.Observable.prototype.purgeListeners.call(this);
46022        this.el.removeAllListeners();
46023     },
46024     /**
46025      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46026      */
46027     show : function(){
46028         this.status_node.addClass("active");
46029         this.showAction();
46030         if(Roo.isOpera){
46031             this.tabPanel.stripWrap.repaint();
46032         }
46033         this.fireEvent("activate", this.tabPanel, this);
46034     },
46035
46036     /**
46037      * Returns true if this tab is the active tab.
46038      * @return {Boolean}
46039      */
46040     isActive : function(){
46041         return this.tabPanel.getActiveTab() == this;
46042     },
46043
46044     /**
46045      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46046      */
46047     hide : function(){
46048         this.status_node.removeClass("active");
46049         this.hideAction();
46050         this.fireEvent("deactivate", this.tabPanel, this);
46051     },
46052
46053     hideAction : function(){
46054         this.bodyEl.hide();
46055         this.bodyEl.setStyle("position", "absolute");
46056         this.bodyEl.setLeft("-20000px");
46057         this.bodyEl.setTop("-20000px");
46058     },
46059
46060     showAction : function(){
46061         this.bodyEl.setStyle("position", "relative");
46062         this.bodyEl.setTop("");
46063         this.bodyEl.setLeft("");
46064         this.bodyEl.show();
46065     },
46066
46067     /**
46068      * Set the tooltip for the tab.
46069      * @param {String} tooltip The tab's tooltip
46070      */
46071     setTooltip : function(text){
46072         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46073             this.textEl.dom.qtip = text;
46074             this.textEl.dom.removeAttribute('title');
46075         }else{
46076             this.textEl.dom.title = text;
46077         }
46078     },
46079
46080     onTabClick : function(e){
46081         e.preventDefault();
46082         this.tabPanel.activate(this.id);
46083     },
46084
46085     onTabMouseDown : function(e){
46086         e.preventDefault();
46087         this.tabPanel.activate(this.id);
46088     },
46089 /*
46090     getWidth : function(){
46091         return this.inner.getWidth();
46092     },
46093
46094     setWidth : function(width){
46095         var iwidth = width - this.linode.getPadding("lr");
46096         this.inner.setWidth(iwidth);
46097         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46098         this.linode.setWidth(width);
46099     },
46100 */
46101     /**
46102      * Show or hide the tab
46103      * @param {Boolean} hidden True to hide or false to show.
46104      */
46105     setHidden : function(hidden){
46106         this.hidden = hidden;
46107         this.linode.setStyle("display", hidden ? "none" : "");
46108     },
46109
46110     /**
46111      * Returns true if this tab is "hidden"
46112      * @return {Boolean}
46113      */
46114     isHidden : function(){
46115         return this.hidden;
46116     },
46117
46118     /**
46119      * Returns the text for this tab
46120      * @return {String}
46121      */
46122     getText : function(){
46123         return this.text;
46124     },
46125     /*
46126     autoSize : function(){
46127         //this.el.beginMeasure();
46128         this.textEl.setWidth(1);
46129         /*
46130          *  #2804 [new] Tabs in Roojs
46131          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46132          */
46133         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46134         //this.el.endMeasure();
46135     //},
46136
46137     /**
46138      * Sets the text for the tab (Note: this also sets the tooltip text)
46139      * @param {String} text The tab's text and tooltip
46140      */
46141     setText : function(text){
46142         this.text = text;
46143         this.textEl.update(text);
46144         this.setTooltip(text);
46145         //if(!this.tabPanel.resizeTabs){
46146         //    this.autoSize();
46147         //}
46148     },
46149     /**
46150      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46151      */
46152     activate : function(){
46153         this.tabPanel.activate(this.id);
46154     },
46155
46156     /**
46157      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46158      */
46159     disable : function(){
46160         if(this.tabPanel.active != this){
46161             this.disabled = true;
46162             this.status_node.addClass("disabled");
46163         }
46164     },
46165
46166     /**
46167      * Enables this TabPanelItem if it was previously disabled.
46168      */
46169     enable : function(){
46170         this.disabled = false;
46171         this.status_node.removeClass("disabled");
46172     },
46173
46174     /**
46175      * Sets the content for this TabPanelItem.
46176      * @param {String} content The content
46177      * @param {Boolean} loadScripts true to look for and load scripts
46178      */
46179     setContent : function(content, loadScripts){
46180         this.bodyEl.update(content, loadScripts);
46181     },
46182
46183     /**
46184      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46185      * @return {Roo.UpdateManager} The UpdateManager
46186      */
46187     getUpdateManager : function(){
46188         return this.bodyEl.getUpdateManager();
46189     },
46190
46191     /**
46192      * Set a URL to be used to load the content for this TabPanelItem.
46193      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46194      * @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)
46195      * @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)
46196      * @return {Roo.UpdateManager} The UpdateManager
46197      */
46198     setUrl : function(url, params, loadOnce){
46199         if(this.refreshDelegate){
46200             this.un('activate', this.refreshDelegate);
46201         }
46202         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46203         this.on("activate", this.refreshDelegate);
46204         return this.bodyEl.getUpdateManager();
46205     },
46206
46207     /** @private */
46208     _handleRefresh : function(url, params, loadOnce){
46209         if(!loadOnce || !this.loaded){
46210             var updater = this.bodyEl.getUpdateManager();
46211             updater.update(url, params, this._setLoaded.createDelegate(this));
46212         }
46213     },
46214
46215     /**
46216      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46217      *   Will fail silently if the setUrl method has not been called.
46218      *   This does not activate the panel, just updates its content.
46219      */
46220     refresh : function(){
46221         if(this.refreshDelegate){
46222            this.loaded = false;
46223            this.refreshDelegate();
46224         }
46225     },
46226
46227     /** @private */
46228     _setLoaded : function(){
46229         this.loaded = true;
46230     },
46231
46232     /** @private */
46233     closeClick : function(e){
46234         var o = {};
46235         e.stopEvent();
46236         this.fireEvent("beforeclose", this, o);
46237         if(o.cancel !== true){
46238             this.tabPanel.removeTab(this.id);
46239         }
46240     },
46241     /**
46242      * The text displayed in the tooltip for the close icon.
46243      * @type String
46244      */
46245     closeText : "Close this tab"
46246 });
46247 /**
46248 *    This script refer to:
46249 *    Title: International Telephone Input
46250 *    Author: Jack O'Connor
46251 *    Code version:  v12.1.12
46252 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46253 **/
46254
46255 Roo.bootstrap.form.PhoneInputData = function() {
46256     var d = [
46257       [
46258         "Afghanistan (‫افغانستان‬‎)",
46259         "af",
46260         "93"
46261       ],
46262       [
46263         "Albania (Shqipëri)",
46264         "al",
46265         "355"
46266       ],
46267       [
46268         "Algeria (‫الجزائر‬‎)",
46269         "dz",
46270         "213"
46271       ],
46272       [
46273         "American Samoa",
46274         "as",
46275         "1684"
46276       ],
46277       [
46278         "Andorra",
46279         "ad",
46280         "376"
46281       ],
46282       [
46283         "Angola",
46284         "ao",
46285         "244"
46286       ],
46287       [
46288         "Anguilla",
46289         "ai",
46290         "1264"
46291       ],
46292       [
46293         "Antigua and Barbuda",
46294         "ag",
46295         "1268"
46296       ],
46297       [
46298         "Argentina",
46299         "ar",
46300         "54"
46301       ],
46302       [
46303         "Armenia (Հայաստան)",
46304         "am",
46305         "374"
46306       ],
46307       [
46308         "Aruba",
46309         "aw",
46310         "297"
46311       ],
46312       [
46313         "Australia",
46314         "au",
46315         "61",
46316         0
46317       ],
46318       [
46319         "Austria (Österreich)",
46320         "at",
46321         "43"
46322       ],
46323       [
46324         "Azerbaijan (Azərbaycan)",
46325         "az",
46326         "994"
46327       ],
46328       [
46329         "Bahamas",
46330         "bs",
46331         "1242"
46332       ],
46333       [
46334         "Bahrain (‫البحرين‬‎)",
46335         "bh",
46336         "973"
46337       ],
46338       [
46339         "Bangladesh (বাংলাদেশ)",
46340         "bd",
46341         "880"
46342       ],
46343       [
46344         "Barbados",
46345         "bb",
46346         "1246"
46347       ],
46348       [
46349         "Belarus (Беларусь)",
46350         "by",
46351         "375"
46352       ],
46353       [
46354         "Belgium (België)",
46355         "be",
46356         "32"
46357       ],
46358       [
46359         "Belize",
46360         "bz",
46361         "501"
46362       ],
46363       [
46364         "Benin (Bénin)",
46365         "bj",
46366         "229"
46367       ],
46368       [
46369         "Bermuda",
46370         "bm",
46371         "1441"
46372       ],
46373       [
46374         "Bhutan (འབྲུག)",
46375         "bt",
46376         "975"
46377       ],
46378       [
46379         "Bolivia",
46380         "bo",
46381         "591"
46382       ],
46383       [
46384         "Bosnia and Herzegovina (Босна и Херцеговина)",
46385         "ba",
46386         "387"
46387       ],
46388       [
46389         "Botswana",
46390         "bw",
46391         "267"
46392       ],
46393       [
46394         "Brazil (Brasil)",
46395         "br",
46396         "55"
46397       ],
46398       [
46399         "British Indian Ocean Territory",
46400         "io",
46401         "246"
46402       ],
46403       [
46404         "British Virgin Islands",
46405         "vg",
46406         "1284"
46407       ],
46408       [
46409         "Brunei",
46410         "bn",
46411         "673"
46412       ],
46413       [
46414         "Bulgaria (България)",
46415         "bg",
46416         "359"
46417       ],
46418       [
46419         "Burkina Faso",
46420         "bf",
46421         "226"
46422       ],
46423       [
46424         "Burundi (Uburundi)",
46425         "bi",
46426         "257"
46427       ],
46428       [
46429         "Cambodia (កម្ពុជា)",
46430         "kh",
46431         "855"
46432       ],
46433       [
46434         "Cameroon (Cameroun)",
46435         "cm",
46436         "237"
46437       ],
46438       [
46439         "Canada",
46440         "ca",
46441         "1",
46442         1,
46443         ["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"]
46444       ],
46445       [
46446         "Cape Verde (Kabu Verdi)",
46447         "cv",
46448         "238"
46449       ],
46450       [
46451         "Caribbean Netherlands",
46452         "bq",
46453         "599",
46454         1
46455       ],
46456       [
46457         "Cayman Islands",
46458         "ky",
46459         "1345"
46460       ],
46461       [
46462         "Central African Republic (République centrafricaine)",
46463         "cf",
46464         "236"
46465       ],
46466       [
46467         "Chad (Tchad)",
46468         "td",
46469         "235"
46470       ],
46471       [
46472         "Chile",
46473         "cl",
46474         "56"
46475       ],
46476       [
46477         "China (中国)",
46478         "cn",
46479         "86"
46480       ],
46481       [
46482         "Christmas Island",
46483         "cx",
46484         "61",
46485         2
46486       ],
46487       [
46488         "Cocos (Keeling) Islands",
46489         "cc",
46490         "61",
46491         1
46492       ],
46493       [
46494         "Colombia",
46495         "co",
46496         "57"
46497       ],
46498       [
46499         "Comoros (‫جزر القمر‬‎)",
46500         "km",
46501         "269"
46502       ],
46503       [
46504         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46505         "cd",
46506         "243"
46507       ],
46508       [
46509         "Congo (Republic) (Congo-Brazzaville)",
46510         "cg",
46511         "242"
46512       ],
46513       [
46514         "Cook Islands",
46515         "ck",
46516         "682"
46517       ],
46518       [
46519         "Costa Rica",
46520         "cr",
46521         "506"
46522       ],
46523       [
46524         "Côte d’Ivoire",
46525         "ci",
46526         "225"
46527       ],
46528       [
46529         "Croatia (Hrvatska)",
46530         "hr",
46531         "385"
46532       ],
46533       [
46534         "Cuba",
46535         "cu",
46536         "53"
46537       ],
46538       [
46539         "Curaçao",
46540         "cw",
46541         "599",
46542         0
46543       ],
46544       [
46545         "Cyprus (Κύπρος)",
46546         "cy",
46547         "357"
46548       ],
46549       [
46550         "Czech Republic (Česká republika)",
46551         "cz",
46552         "420"
46553       ],
46554       [
46555         "Denmark (Danmark)",
46556         "dk",
46557         "45"
46558       ],
46559       [
46560         "Djibouti",
46561         "dj",
46562         "253"
46563       ],
46564       [
46565         "Dominica",
46566         "dm",
46567         "1767"
46568       ],
46569       [
46570         "Dominican Republic (República Dominicana)",
46571         "do",
46572         "1",
46573         2,
46574         ["809", "829", "849"]
46575       ],
46576       [
46577         "Ecuador",
46578         "ec",
46579         "593"
46580       ],
46581       [
46582         "Egypt (‫مصر‬‎)",
46583         "eg",
46584         "20"
46585       ],
46586       [
46587         "El Salvador",
46588         "sv",
46589         "503"
46590       ],
46591       [
46592         "Equatorial Guinea (Guinea Ecuatorial)",
46593         "gq",
46594         "240"
46595       ],
46596       [
46597         "Eritrea",
46598         "er",
46599         "291"
46600       ],
46601       [
46602         "Estonia (Eesti)",
46603         "ee",
46604         "372"
46605       ],
46606       [
46607         "Ethiopia",
46608         "et",
46609         "251"
46610       ],
46611       [
46612         "Falkland Islands (Islas Malvinas)",
46613         "fk",
46614         "500"
46615       ],
46616       [
46617         "Faroe Islands (Føroyar)",
46618         "fo",
46619         "298"
46620       ],
46621       [
46622         "Fiji",
46623         "fj",
46624         "679"
46625       ],
46626       [
46627         "Finland (Suomi)",
46628         "fi",
46629         "358",
46630         0
46631       ],
46632       [
46633         "France",
46634         "fr",
46635         "33"
46636       ],
46637       [
46638         "French Guiana (Guyane française)",
46639         "gf",
46640         "594"
46641       ],
46642       [
46643         "French Polynesia (Polynésie française)",
46644         "pf",
46645         "689"
46646       ],
46647       [
46648         "Gabon",
46649         "ga",
46650         "241"
46651       ],
46652       [
46653         "Gambia",
46654         "gm",
46655         "220"
46656       ],
46657       [
46658         "Georgia (საქართველო)",
46659         "ge",
46660         "995"
46661       ],
46662       [
46663         "Germany (Deutschland)",
46664         "de",
46665         "49"
46666       ],
46667       [
46668         "Ghana (Gaana)",
46669         "gh",
46670         "233"
46671       ],
46672       [
46673         "Gibraltar",
46674         "gi",
46675         "350"
46676       ],
46677       [
46678         "Greece (Ελλάδα)",
46679         "gr",
46680         "30"
46681       ],
46682       [
46683         "Greenland (Kalaallit Nunaat)",
46684         "gl",
46685         "299"
46686       ],
46687       [
46688         "Grenada",
46689         "gd",
46690         "1473"
46691       ],
46692       [
46693         "Guadeloupe",
46694         "gp",
46695         "590",
46696         0
46697       ],
46698       [
46699         "Guam",
46700         "gu",
46701         "1671"
46702       ],
46703       [
46704         "Guatemala",
46705         "gt",
46706         "502"
46707       ],
46708       [
46709         "Guernsey",
46710         "gg",
46711         "44",
46712         1
46713       ],
46714       [
46715         "Guinea (Guinée)",
46716         "gn",
46717         "224"
46718       ],
46719       [
46720         "Guinea-Bissau (Guiné Bissau)",
46721         "gw",
46722         "245"
46723       ],
46724       [
46725         "Guyana",
46726         "gy",
46727         "592"
46728       ],
46729       [
46730         "Haiti",
46731         "ht",
46732         "509"
46733       ],
46734       [
46735         "Honduras",
46736         "hn",
46737         "504"
46738       ],
46739       [
46740         "Hong Kong (香港)",
46741         "hk",
46742         "852"
46743       ],
46744       [
46745         "Hungary (Magyarország)",
46746         "hu",
46747         "36"
46748       ],
46749       [
46750         "Iceland (Ísland)",
46751         "is",
46752         "354"
46753       ],
46754       [
46755         "India (भारत)",
46756         "in",
46757         "91"
46758       ],
46759       [
46760         "Indonesia",
46761         "id",
46762         "62"
46763       ],
46764       [
46765         "Iran (‫ایران‬‎)",
46766         "ir",
46767         "98"
46768       ],
46769       [
46770         "Iraq (‫العراق‬‎)",
46771         "iq",
46772         "964"
46773       ],
46774       [
46775         "Ireland",
46776         "ie",
46777         "353"
46778       ],
46779       [
46780         "Isle of Man",
46781         "im",
46782         "44",
46783         2
46784       ],
46785       [
46786         "Israel (‫ישראל‬‎)",
46787         "il",
46788         "972"
46789       ],
46790       [
46791         "Italy (Italia)",
46792         "it",
46793         "39",
46794         0
46795       ],
46796       [
46797         "Jamaica",
46798         "jm",
46799         "1876"
46800       ],
46801       [
46802         "Japan (日本)",
46803         "jp",
46804         "81"
46805       ],
46806       [
46807         "Jersey",
46808         "je",
46809         "44",
46810         3
46811       ],
46812       [
46813         "Jordan (‫الأردن‬‎)",
46814         "jo",
46815         "962"
46816       ],
46817       [
46818         "Kazakhstan (Казахстан)",
46819         "kz",
46820         "7",
46821         1
46822       ],
46823       [
46824         "Kenya",
46825         "ke",
46826         "254"
46827       ],
46828       [
46829         "Kiribati",
46830         "ki",
46831         "686"
46832       ],
46833       [
46834         "Kosovo",
46835         "xk",
46836         "383"
46837       ],
46838       [
46839         "Kuwait (‫الكويت‬‎)",
46840         "kw",
46841         "965"
46842       ],
46843       [
46844         "Kyrgyzstan (Кыргызстан)",
46845         "kg",
46846         "996"
46847       ],
46848       [
46849         "Laos (ລາວ)",
46850         "la",
46851         "856"
46852       ],
46853       [
46854         "Latvia (Latvija)",
46855         "lv",
46856         "371"
46857       ],
46858       [
46859         "Lebanon (‫لبنان‬‎)",
46860         "lb",
46861         "961"
46862       ],
46863       [
46864         "Lesotho",
46865         "ls",
46866         "266"
46867       ],
46868       [
46869         "Liberia",
46870         "lr",
46871         "231"
46872       ],
46873       [
46874         "Libya (‫ليبيا‬‎)",
46875         "ly",
46876         "218"
46877       ],
46878       [
46879         "Liechtenstein",
46880         "li",
46881         "423"
46882       ],
46883       [
46884         "Lithuania (Lietuva)",
46885         "lt",
46886         "370"
46887       ],
46888       [
46889         "Luxembourg",
46890         "lu",
46891         "352"
46892       ],
46893       [
46894         "Macau (澳門)",
46895         "mo",
46896         "853"
46897       ],
46898       [
46899         "Macedonia (FYROM) (Македонија)",
46900         "mk",
46901         "389"
46902       ],
46903       [
46904         "Madagascar (Madagasikara)",
46905         "mg",
46906         "261"
46907       ],
46908       [
46909         "Malawi",
46910         "mw",
46911         "265"
46912       ],
46913       [
46914         "Malaysia",
46915         "my",
46916         "60"
46917       ],
46918       [
46919         "Maldives",
46920         "mv",
46921         "960"
46922       ],
46923       [
46924         "Mali",
46925         "ml",
46926         "223"
46927       ],
46928       [
46929         "Malta",
46930         "mt",
46931         "356"
46932       ],
46933       [
46934         "Marshall Islands",
46935         "mh",
46936         "692"
46937       ],
46938       [
46939         "Martinique",
46940         "mq",
46941         "596"
46942       ],
46943       [
46944         "Mauritania (‫موريتانيا‬‎)",
46945         "mr",
46946         "222"
46947       ],
46948       [
46949         "Mauritius (Moris)",
46950         "mu",
46951         "230"
46952       ],
46953       [
46954         "Mayotte",
46955         "yt",
46956         "262",
46957         1
46958       ],
46959       [
46960         "Mexico (México)",
46961         "mx",
46962         "52"
46963       ],
46964       [
46965         "Micronesia",
46966         "fm",
46967         "691"
46968       ],
46969       [
46970         "Moldova (Republica Moldova)",
46971         "md",
46972         "373"
46973       ],
46974       [
46975         "Monaco",
46976         "mc",
46977         "377"
46978       ],
46979       [
46980         "Mongolia (Монгол)",
46981         "mn",
46982         "976"
46983       ],
46984       [
46985         "Montenegro (Crna Gora)",
46986         "me",
46987         "382"
46988       ],
46989       [
46990         "Montserrat",
46991         "ms",
46992         "1664"
46993       ],
46994       [
46995         "Morocco (‫المغرب‬‎)",
46996         "ma",
46997         "212",
46998         0
46999       ],
47000       [
47001         "Mozambique (Moçambique)",
47002         "mz",
47003         "258"
47004       ],
47005       [
47006         "Myanmar (Burma) (မြန်မာ)",
47007         "mm",
47008         "95"
47009       ],
47010       [
47011         "Namibia (Namibië)",
47012         "na",
47013         "264"
47014       ],
47015       [
47016         "Nauru",
47017         "nr",
47018         "674"
47019       ],
47020       [
47021         "Nepal (नेपाल)",
47022         "np",
47023         "977"
47024       ],
47025       [
47026         "Netherlands (Nederland)",
47027         "nl",
47028         "31"
47029       ],
47030       [
47031         "New Caledonia (Nouvelle-Calédonie)",
47032         "nc",
47033         "687"
47034       ],
47035       [
47036         "New Zealand",
47037         "nz",
47038         "64"
47039       ],
47040       [
47041         "Nicaragua",
47042         "ni",
47043         "505"
47044       ],
47045       [
47046         "Niger (Nijar)",
47047         "ne",
47048         "227"
47049       ],
47050       [
47051         "Nigeria",
47052         "ng",
47053         "234"
47054       ],
47055       [
47056         "Niue",
47057         "nu",
47058         "683"
47059       ],
47060       [
47061         "Norfolk Island",
47062         "nf",
47063         "672"
47064       ],
47065       [
47066         "North Korea (조선 민주주의 인민 공화국)",
47067         "kp",
47068         "850"
47069       ],
47070       [
47071         "Northern Mariana Islands",
47072         "mp",
47073         "1670"
47074       ],
47075       [
47076         "Norway (Norge)",
47077         "no",
47078         "47",
47079         0
47080       ],
47081       [
47082         "Oman (‫عُمان‬‎)",
47083         "om",
47084         "968"
47085       ],
47086       [
47087         "Pakistan (‫پاکستان‬‎)",
47088         "pk",
47089         "92"
47090       ],
47091       [
47092         "Palau",
47093         "pw",
47094         "680"
47095       ],
47096       [
47097         "Palestine (‫فلسطين‬‎)",
47098         "ps",
47099         "970"
47100       ],
47101       [
47102         "Panama (Panamá)",
47103         "pa",
47104         "507"
47105       ],
47106       [
47107         "Papua New Guinea",
47108         "pg",
47109         "675"
47110       ],
47111       [
47112         "Paraguay",
47113         "py",
47114         "595"
47115       ],
47116       [
47117         "Peru (Perú)",
47118         "pe",
47119         "51"
47120       ],
47121       [
47122         "Philippines",
47123         "ph",
47124         "63"
47125       ],
47126       [
47127         "Poland (Polska)",
47128         "pl",
47129         "48"
47130       ],
47131       [
47132         "Portugal",
47133         "pt",
47134         "351"
47135       ],
47136       [
47137         "Puerto Rico",
47138         "pr",
47139         "1",
47140         3,
47141         ["787", "939"]
47142       ],
47143       [
47144         "Qatar (‫قطر‬‎)",
47145         "qa",
47146         "974"
47147       ],
47148       [
47149         "Réunion (La Réunion)",
47150         "re",
47151         "262",
47152         0
47153       ],
47154       [
47155         "Romania (România)",
47156         "ro",
47157         "40"
47158       ],
47159       [
47160         "Russia (Россия)",
47161         "ru",
47162         "7",
47163         0
47164       ],
47165       [
47166         "Rwanda",
47167         "rw",
47168         "250"
47169       ],
47170       [
47171         "Saint Barthélemy",
47172         "bl",
47173         "590",
47174         1
47175       ],
47176       [
47177         "Saint Helena",
47178         "sh",
47179         "290"
47180       ],
47181       [
47182         "Saint Kitts and Nevis",
47183         "kn",
47184         "1869"
47185       ],
47186       [
47187         "Saint Lucia",
47188         "lc",
47189         "1758"
47190       ],
47191       [
47192         "Saint Martin (Saint-Martin (partie française))",
47193         "mf",
47194         "590",
47195         2
47196       ],
47197       [
47198         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47199         "pm",
47200         "508"
47201       ],
47202       [
47203         "Saint Vincent and the Grenadines",
47204         "vc",
47205         "1784"
47206       ],
47207       [
47208         "Samoa",
47209         "ws",
47210         "685"
47211       ],
47212       [
47213         "San Marino",
47214         "sm",
47215         "378"
47216       ],
47217       [
47218         "São Tomé and Príncipe (São Tomé e Príncipe)",
47219         "st",
47220         "239"
47221       ],
47222       [
47223         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47224         "sa",
47225         "966"
47226       ],
47227       [
47228         "Senegal (Sénégal)",
47229         "sn",
47230         "221"
47231       ],
47232       [
47233         "Serbia (Србија)",
47234         "rs",
47235         "381"
47236       ],
47237       [
47238         "Seychelles",
47239         "sc",
47240         "248"
47241       ],
47242       [
47243         "Sierra Leone",
47244         "sl",
47245         "232"
47246       ],
47247       [
47248         "Singapore",
47249         "sg",
47250         "65"
47251       ],
47252       [
47253         "Sint Maarten",
47254         "sx",
47255         "1721"
47256       ],
47257       [
47258         "Slovakia (Slovensko)",
47259         "sk",
47260         "421"
47261       ],
47262       [
47263         "Slovenia (Slovenija)",
47264         "si",
47265         "386"
47266       ],
47267       [
47268         "Solomon Islands",
47269         "sb",
47270         "677"
47271       ],
47272       [
47273         "Somalia (Soomaaliya)",
47274         "so",
47275         "252"
47276       ],
47277       [
47278         "South Africa",
47279         "za",
47280         "27"
47281       ],
47282       [
47283         "South Korea (대한민국)",
47284         "kr",
47285         "82"
47286       ],
47287       [
47288         "South Sudan (‫جنوب السودان‬‎)",
47289         "ss",
47290         "211"
47291       ],
47292       [
47293         "Spain (España)",
47294         "es",
47295         "34"
47296       ],
47297       [
47298         "Sri Lanka (ශ්‍රී ලංකාව)",
47299         "lk",
47300         "94"
47301       ],
47302       [
47303         "Sudan (‫السودان‬‎)",
47304         "sd",
47305         "249"
47306       ],
47307       [
47308         "Suriname",
47309         "sr",
47310         "597"
47311       ],
47312       [
47313         "Svalbard and Jan Mayen",
47314         "sj",
47315         "47",
47316         1
47317       ],
47318       [
47319         "Swaziland",
47320         "sz",
47321         "268"
47322       ],
47323       [
47324         "Sweden (Sverige)",
47325         "se",
47326         "46"
47327       ],
47328       [
47329         "Switzerland (Schweiz)",
47330         "ch",
47331         "41"
47332       ],
47333       [
47334         "Syria (‫سوريا‬‎)",
47335         "sy",
47336         "963"
47337       ],
47338       [
47339         "Taiwan (台灣)",
47340         "tw",
47341         "886"
47342       ],
47343       [
47344         "Tajikistan",
47345         "tj",
47346         "992"
47347       ],
47348       [
47349         "Tanzania",
47350         "tz",
47351         "255"
47352       ],
47353       [
47354         "Thailand (ไทย)",
47355         "th",
47356         "66"
47357       ],
47358       [
47359         "Timor-Leste",
47360         "tl",
47361         "670"
47362       ],
47363       [
47364         "Togo",
47365         "tg",
47366         "228"
47367       ],
47368       [
47369         "Tokelau",
47370         "tk",
47371         "690"
47372       ],
47373       [
47374         "Tonga",
47375         "to",
47376         "676"
47377       ],
47378       [
47379         "Trinidad and Tobago",
47380         "tt",
47381         "1868"
47382       ],
47383       [
47384         "Tunisia (‫تونس‬‎)",
47385         "tn",
47386         "216"
47387       ],
47388       [
47389         "Turkey (Türkiye)",
47390         "tr",
47391         "90"
47392       ],
47393       [
47394         "Turkmenistan",
47395         "tm",
47396         "993"
47397       ],
47398       [
47399         "Turks and Caicos Islands",
47400         "tc",
47401         "1649"
47402       ],
47403       [
47404         "Tuvalu",
47405         "tv",
47406         "688"
47407       ],
47408       [
47409         "U.S. Virgin Islands",
47410         "vi",
47411         "1340"
47412       ],
47413       [
47414         "Uganda",
47415         "ug",
47416         "256"
47417       ],
47418       [
47419         "Ukraine (Україна)",
47420         "ua",
47421         "380"
47422       ],
47423       [
47424         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47425         "ae",
47426         "971"
47427       ],
47428       [
47429         "United Kingdom",
47430         "gb",
47431         "44",
47432         0
47433       ],
47434       [
47435         "United States",
47436         "us",
47437         "1",
47438         0
47439       ],
47440       [
47441         "Uruguay",
47442         "uy",
47443         "598"
47444       ],
47445       [
47446         "Uzbekistan (Oʻzbekiston)",
47447         "uz",
47448         "998"
47449       ],
47450       [
47451         "Vanuatu",
47452         "vu",
47453         "678"
47454       ],
47455       [
47456         "Vatican City (Città del Vaticano)",
47457         "va",
47458         "39",
47459         1
47460       ],
47461       [
47462         "Venezuela",
47463         "ve",
47464         "58"
47465       ],
47466       [
47467         "Vietnam (Việt Nam)",
47468         "vn",
47469         "84"
47470       ],
47471       [
47472         "Wallis and Futuna (Wallis-et-Futuna)",
47473         "wf",
47474         "681"
47475       ],
47476       [
47477         "Western Sahara (‫الصحراء الغربية‬‎)",
47478         "eh",
47479         "212",
47480         1
47481       ],
47482       [
47483         "Yemen (‫اليمن‬‎)",
47484         "ye",
47485         "967"
47486       ],
47487       [
47488         "Zambia",
47489         "zm",
47490         "260"
47491       ],
47492       [
47493         "Zimbabwe",
47494         "zw",
47495         "263"
47496       ],
47497       [
47498         "Åland Islands",
47499         "ax",
47500         "358",
47501         1
47502       ]
47503   ];
47504   
47505   return d;
47506 }/**
47507 *    This script refer to:
47508 *    Title: International Telephone Input
47509 *    Author: Jack O'Connor
47510 *    Code version:  v12.1.12
47511 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47512 **/
47513
47514 /**
47515  * @class Roo.bootstrap.form.PhoneInput
47516  * @extends Roo.bootstrap.form.TriggerField
47517  * An input with International dial-code selection
47518  
47519  * @cfg {String} defaultDialCode default '+852'
47520  * @cfg {Array} preferedCountries default []
47521   
47522  * @constructor
47523  * Create a new PhoneInput.
47524  * @param {Object} config Configuration options
47525  */
47526
47527 Roo.bootstrap.form.PhoneInput = function(config) {
47528     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47529 };
47530
47531 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47532         /**
47533         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47534         */
47535         listWidth: undefined,
47536         
47537         selectedClass: 'active',
47538         
47539         invalidClass : "has-warning",
47540         
47541         validClass: 'has-success',
47542         
47543         allowed: '0123456789',
47544         
47545         max_length: 15,
47546         
47547         /**
47548          * @cfg {String} defaultDialCode The default dial code when initializing the input
47549          */
47550         defaultDialCode: '+852',
47551         
47552         /**
47553          * @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
47554          */
47555         preferedCountries: false,
47556         
47557         getAutoCreate : function()
47558         {
47559             var data = Roo.bootstrap.form.PhoneInputData();
47560             var align = this.labelAlign || this.parentLabelAlign();
47561             var id = Roo.id();
47562             
47563             this.allCountries = [];
47564             this.dialCodeMapping = [];
47565             
47566             for (var i = 0; i < data.length; i++) {
47567               var c = data[i];
47568               this.allCountries[i] = {
47569                 name: c[0],
47570                 iso2: c[1],
47571                 dialCode: c[2],
47572                 priority: c[3] || 0,
47573                 areaCodes: c[4] || null
47574               };
47575               this.dialCodeMapping[c[2]] = {
47576                   name: c[0],
47577                   iso2: c[1],
47578                   priority: c[3] || 0,
47579                   areaCodes: c[4] || null
47580               };
47581             }
47582             
47583             var cfg = {
47584                 cls: 'form-group',
47585                 cn: []
47586             };
47587             
47588             var input =  {
47589                 tag: 'input',
47590                 id : id,
47591                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47592                 maxlength: this.max_length,
47593                 cls : 'form-control tel-input',
47594                 autocomplete: 'new-password'
47595             };
47596             
47597             var hiddenInput = {
47598                 tag: 'input',
47599                 type: 'hidden',
47600                 cls: 'hidden-tel-input'
47601             };
47602             
47603             if (this.name) {
47604                 hiddenInput.name = this.name;
47605             }
47606             
47607             if (this.disabled) {
47608                 input.disabled = true;
47609             }
47610             
47611             var flag_container = {
47612                 tag: 'div',
47613                 cls: 'flag-box',
47614                 cn: [
47615                     {
47616                         tag: 'div',
47617                         cls: 'flag'
47618                     },
47619                     {
47620                         tag: 'div',
47621                         cls: 'caret'
47622                     }
47623                 ]
47624             };
47625             
47626             var box = {
47627                 tag: 'div',
47628                 cls: this.hasFeedback ? 'has-feedback' : '',
47629                 cn: [
47630                     hiddenInput,
47631                     input,
47632                     {
47633                         tag: 'input',
47634                         cls: 'dial-code-holder',
47635                         disabled: true
47636                     }
47637                 ]
47638             };
47639             
47640             var container = {
47641                 cls: 'roo-select2-container input-group',
47642                 cn: [
47643                     flag_container,
47644                     box
47645                 ]
47646             };
47647             
47648             if (this.fieldLabel.length) {
47649                 var indicator = {
47650                     tag: 'i',
47651                     tooltip: 'This field is required'
47652                 };
47653                 
47654                 var label = {
47655                     tag: 'label',
47656                     'for':  id,
47657                     cls: 'control-label',
47658                     cn: []
47659                 };
47660                 
47661                 var label_text = {
47662                     tag: 'span',
47663                     html: this.fieldLabel
47664                 };
47665                 
47666                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47667                 label.cn = [
47668                     indicator,
47669                     label_text
47670                 ];
47671                 
47672                 if(this.indicatorpos == 'right') {
47673                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47674                     label.cn = [
47675                         label_text,
47676                         indicator
47677                     ];
47678                 }
47679                 
47680                 if(align == 'left') {
47681                     container = {
47682                         tag: 'div',
47683                         cn: [
47684                             container
47685                         ]
47686                     };
47687                     
47688                     if(this.labelWidth > 12){
47689                         label.style = "width: " + this.labelWidth + 'px';
47690                     }
47691                     if(this.labelWidth < 13 && this.labelmd == 0){
47692                         this.labelmd = this.labelWidth;
47693                     }
47694                     if(this.labellg > 0){
47695                         label.cls += ' col-lg-' + this.labellg;
47696                         input.cls += ' col-lg-' + (12 - this.labellg);
47697                     }
47698                     if(this.labelmd > 0){
47699                         label.cls += ' col-md-' + this.labelmd;
47700                         container.cls += ' col-md-' + (12 - this.labelmd);
47701                     }
47702                     if(this.labelsm > 0){
47703                         label.cls += ' col-sm-' + this.labelsm;
47704                         container.cls += ' col-sm-' + (12 - this.labelsm);
47705                     }
47706                     if(this.labelxs > 0){
47707                         label.cls += ' col-xs-' + this.labelxs;
47708                         container.cls += ' col-xs-' + (12 - this.labelxs);
47709                     }
47710                 }
47711             }
47712             
47713             cfg.cn = [
47714                 label,
47715                 container
47716             ];
47717             
47718             var settings = this;
47719             
47720             ['xs','sm','md','lg'].map(function(size){
47721                 if (settings[size]) {
47722                     cfg.cls += ' col-' + size + '-' + settings[size];
47723                 }
47724             });
47725             
47726             this.store = new Roo.data.Store({
47727                 proxy : new Roo.data.MemoryProxy({}),
47728                 reader : new Roo.data.JsonReader({
47729                     fields : [
47730                         {
47731                             'name' : 'name',
47732                             'type' : 'string'
47733                         },
47734                         {
47735                             'name' : 'iso2',
47736                             'type' : 'string'
47737                         },
47738                         {
47739                             'name' : 'dialCode',
47740                             'type' : 'string'
47741                         },
47742                         {
47743                             'name' : 'priority',
47744                             'type' : 'string'
47745                         },
47746                         {
47747                             'name' : 'areaCodes',
47748                             'type' : 'string'
47749                         }
47750                     ]
47751                 })
47752             });
47753             
47754             if(!this.preferedCountries) {
47755                 this.preferedCountries = [
47756                     'hk',
47757                     'gb',
47758                     'us'
47759                 ];
47760             }
47761             
47762             var p = this.preferedCountries.reverse();
47763             
47764             if(p) {
47765                 for (var i = 0; i < p.length; i++) {
47766                     for (var j = 0; j < this.allCountries.length; j++) {
47767                         if(this.allCountries[j].iso2 == p[i]) {
47768                             var t = this.allCountries[j];
47769                             this.allCountries.splice(j,1);
47770                             this.allCountries.unshift(t);
47771                         }
47772                     } 
47773                 }
47774             }
47775             
47776             this.store.proxy.data = {
47777                 success: true,
47778                 data: this.allCountries
47779             };
47780             
47781             return cfg;
47782         },
47783         
47784         initEvents : function()
47785         {
47786             this.createList();
47787             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47788             
47789             this.indicator = this.indicatorEl();
47790             this.flag = this.flagEl();
47791             this.dialCodeHolder = this.dialCodeHolderEl();
47792             
47793             this.trigger = this.el.select('div.flag-box',true).first();
47794             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47795             
47796             var _this = this;
47797             
47798             (function(){
47799                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47800                 _this.list.setWidth(lw);
47801             }).defer(100);
47802             
47803             this.list.on('mouseover', this.onViewOver, this);
47804             this.list.on('mousemove', this.onViewMove, this);
47805             this.inputEl().on("keyup", this.onKeyUp, this);
47806             this.inputEl().on("keypress", this.onKeyPress, this);
47807             
47808             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47809
47810             this.view = new Roo.View(this.list, this.tpl, {
47811                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47812             });
47813             
47814             this.view.on('click', this.onViewClick, this);
47815             this.setValue(this.defaultDialCode);
47816         },
47817         
47818         onTriggerClick : function(e)
47819         {
47820             Roo.log('trigger click');
47821             if(this.disabled){
47822                 return;
47823             }
47824             
47825             if(this.isExpanded()){
47826                 this.collapse();
47827                 this.hasFocus = false;
47828             }else {
47829                 this.store.load({});
47830                 this.hasFocus = true;
47831                 this.expand();
47832             }
47833         },
47834         
47835         isExpanded : function()
47836         {
47837             return this.list.isVisible();
47838         },
47839         
47840         collapse : function()
47841         {
47842             if(!this.isExpanded()){
47843                 return;
47844             }
47845             this.list.hide();
47846             Roo.get(document).un('mousedown', this.collapseIf, this);
47847             Roo.get(document).un('mousewheel', this.collapseIf, this);
47848             this.fireEvent('collapse', this);
47849             this.validate();
47850         },
47851         
47852         expand : function()
47853         {
47854             Roo.log('expand');
47855
47856             if(this.isExpanded() || !this.hasFocus){
47857                 return;
47858             }
47859             
47860             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47861             this.list.setWidth(lw);
47862             
47863             this.list.show();
47864             this.restrictHeight();
47865             
47866             Roo.get(document).on('mousedown', this.collapseIf, this);
47867             Roo.get(document).on('mousewheel', this.collapseIf, this);
47868             
47869             this.fireEvent('expand', this);
47870         },
47871         
47872         restrictHeight : function()
47873         {
47874             this.list.alignTo(this.inputEl(), this.listAlign);
47875             this.list.alignTo(this.inputEl(), this.listAlign);
47876         },
47877         
47878         onViewOver : function(e, t)
47879         {
47880             if(this.inKeyMode){
47881                 return;
47882             }
47883             var item = this.view.findItemFromChild(t);
47884             
47885             if(item){
47886                 var index = this.view.indexOf(item);
47887                 this.select(index, false);
47888             }
47889         },
47890
47891         // private
47892         onViewClick : function(view, doFocus, el, e)
47893         {
47894             var index = this.view.getSelectedIndexes()[0];
47895             
47896             var r = this.store.getAt(index);
47897             
47898             if(r){
47899                 this.onSelect(r, index);
47900             }
47901             if(doFocus !== false && !this.blockFocus){
47902                 this.inputEl().focus();
47903             }
47904         },
47905         
47906         onViewMove : function(e, t)
47907         {
47908             this.inKeyMode = false;
47909         },
47910         
47911         select : function(index, scrollIntoView)
47912         {
47913             this.selectedIndex = index;
47914             this.view.select(index);
47915             if(scrollIntoView !== false){
47916                 var el = this.view.getNode(index);
47917                 if(el){
47918                     this.list.scrollChildIntoView(el, false);
47919                 }
47920             }
47921         },
47922         
47923         createList : function()
47924         {
47925             this.list = Roo.get(document.body).createChild({
47926                 tag: 'ul',
47927                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47928                 style: 'display:none'
47929             });
47930             
47931             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47932         },
47933         
47934         collapseIf : function(e)
47935         {
47936             var in_combo  = e.within(this.el);
47937             var in_list =  e.within(this.list);
47938             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47939             
47940             if (in_combo || in_list || is_list) {
47941                 return;
47942             }
47943             this.collapse();
47944         },
47945         
47946         onSelect : function(record, index)
47947         {
47948             if(this.fireEvent('beforeselect', this, record, index) !== false){
47949                 
47950                 this.setFlagClass(record.data.iso2);
47951                 this.setDialCode(record.data.dialCode);
47952                 this.hasFocus = false;
47953                 this.collapse();
47954                 this.fireEvent('select', this, record, index);
47955             }
47956         },
47957         
47958         flagEl : function()
47959         {
47960             var flag = this.el.select('div.flag',true).first();
47961             if(!flag){
47962                 return false;
47963             }
47964             return flag;
47965         },
47966         
47967         dialCodeHolderEl : function()
47968         {
47969             var d = this.el.select('input.dial-code-holder',true).first();
47970             if(!d){
47971                 return false;
47972             }
47973             return d;
47974         },
47975         
47976         setDialCode : function(v)
47977         {
47978             this.dialCodeHolder.dom.value = '+'+v;
47979         },
47980         
47981         setFlagClass : function(n)
47982         {
47983             this.flag.dom.className = 'flag '+n;
47984         },
47985         
47986         getValue : function()
47987         {
47988             var v = this.inputEl().getValue();
47989             if(this.dialCodeHolder) {
47990                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47991             }
47992             return v;
47993         },
47994         
47995         setValue : function(v)
47996         {
47997             var d = this.getDialCode(v);
47998             
47999             //invalid dial code
48000             if(v.length == 0 || !d || d.length == 0) {
48001                 if(this.rendered){
48002                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48003                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48004                 }
48005                 return;
48006             }
48007             
48008             //valid dial code
48009             this.setFlagClass(this.dialCodeMapping[d].iso2);
48010             this.setDialCode(d);
48011             this.inputEl().dom.value = v.replace('+'+d,'');
48012             this.hiddenEl().dom.value = this.getValue();
48013             
48014             this.validate();
48015         },
48016         
48017         getDialCode : function(v)
48018         {
48019             v = v ||  '';
48020             
48021             if (v.length == 0) {
48022                 return this.dialCodeHolder.dom.value;
48023             }
48024             
48025             var dialCode = "";
48026             if (v.charAt(0) != "+") {
48027                 return false;
48028             }
48029             var numericChars = "";
48030             for (var i = 1; i < v.length; i++) {
48031               var c = v.charAt(i);
48032               if (!isNaN(c)) {
48033                 numericChars += c;
48034                 if (this.dialCodeMapping[numericChars]) {
48035                   dialCode = v.substr(1, i);
48036                 }
48037                 if (numericChars.length == 4) {
48038                   break;
48039                 }
48040               }
48041             }
48042             return dialCode;
48043         },
48044         
48045         reset : function()
48046         {
48047             this.setValue(this.defaultDialCode);
48048             this.validate();
48049         },
48050         
48051         hiddenEl : function()
48052         {
48053             return this.el.select('input.hidden-tel-input',true).first();
48054         },
48055         
48056         // after setting val
48057         onKeyUp : function(e){
48058             this.setValue(this.getValue());
48059         },
48060         
48061         onKeyPress : function(e){
48062             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48063                 e.stopEvent();
48064             }
48065         }
48066         
48067 });
48068 /**
48069  * @class Roo.bootstrap.form.MoneyField
48070  * @extends Roo.bootstrap.form.ComboBox
48071  * Bootstrap MoneyField class
48072  * 
48073  * @constructor
48074  * Create a new MoneyField.
48075  * @param {Object} config Configuration options
48076  */
48077
48078 Roo.bootstrap.form.MoneyField = function(config) {
48079     
48080     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48081     
48082 };
48083
48084 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48085     
48086     /**
48087      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48088      */
48089     allowDecimals : true,
48090     /**
48091      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48092      */
48093     decimalSeparator : ".",
48094     /**
48095      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48096      */
48097     decimalPrecision : 0,
48098     /**
48099      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48100      */
48101     allowNegative : true,
48102     /**
48103      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48104      */
48105     allowZero: true,
48106     /**
48107      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48108      */
48109     minValue : Number.NEGATIVE_INFINITY,
48110     /**
48111      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48112      */
48113     maxValue : Number.MAX_VALUE,
48114     /**
48115      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48116      */
48117     minText : "The minimum value for this field is {0}",
48118     /**
48119      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48120      */
48121     maxText : "The maximum value for this field is {0}",
48122     /**
48123      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48124      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48125      */
48126     nanText : "{0} is not a valid number",
48127     /**
48128      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48129      */
48130     castInt : true,
48131     /**
48132      * @cfg {String} defaults currency of the MoneyField
48133      * value should be in lkey
48134      */
48135     defaultCurrency : false,
48136     /**
48137      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48138      */
48139     thousandsDelimiter : false,
48140     /**
48141      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48142      */
48143     max_length: false,
48144     
48145     inputlg : 9,
48146     inputmd : 9,
48147     inputsm : 9,
48148     inputxs : 6,
48149      /**
48150      * @cfg {Roo.data.Store} store  Store to lookup currency??
48151      */
48152     store : false,
48153     
48154     getAutoCreate : function()
48155     {
48156         var align = this.labelAlign || this.parentLabelAlign();
48157         
48158         var id = Roo.id();
48159
48160         var cfg = {
48161             cls: 'form-group',
48162             cn: []
48163         };
48164
48165         var input =  {
48166             tag: 'input',
48167             id : id,
48168             cls : 'form-control roo-money-amount-input',
48169             autocomplete: 'new-password'
48170         };
48171         
48172         var hiddenInput = {
48173             tag: 'input',
48174             type: 'hidden',
48175             id: Roo.id(),
48176             cls: 'hidden-number-input'
48177         };
48178         
48179         if(this.max_length) {
48180             input.maxlength = this.max_length; 
48181         }
48182         
48183         if (this.name) {
48184             hiddenInput.name = this.name;
48185         }
48186
48187         if (this.disabled) {
48188             input.disabled = true;
48189         }
48190
48191         var clg = 12 - this.inputlg;
48192         var cmd = 12 - this.inputmd;
48193         var csm = 12 - this.inputsm;
48194         var cxs = 12 - this.inputxs;
48195         
48196         var container = {
48197             tag : 'div',
48198             cls : 'row roo-money-field',
48199             cn : [
48200                 {
48201                     tag : 'div',
48202                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48203                     cn : [
48204                         {
48205                             tag : 'div',
48206                             cls: 'roo-select2-container input-group',
48207                             cn: [
48208                                 {
48209                                     tag : 'input',
48210                                     cls : 'form-control roo-money-currency-input',
48211                                     autocomplete: 'new-password',
48212                                     readOnly : 1,
48213                                     name : this.currencyName
48214                                 },
48215                                 {
48216                                     tag :'span',
48217                                     cls : 'input-group-addon',
48218                                     cn : [
48219                                         {
48220                                             tag: 'span',
48221                                             cls: 'caret'
48222                                         }
48223                                     ]
48224                                 }
48225                             ]
48226                         }
48227                     ]
48228                 },
48229                 {
48230                     tag : 'div',
48231                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48232                     cn : [
48233                         {
48234                             tag: 'div',
48235                             cls: this.hasFeedback ? 'has-feedback' : '',
48236                             cn: [
48237                                 input
48238                             ]
48239                         }
48240                     ]
48241                 }
48242             ]
48243             
48244         };
48245         
48246         if (this.fieldLabel.length) {
48247             var indicator = {
48248                 tag: 'i',
48249                 tooltip: 'This field is required'
48250             };
48251
48252             var label = {
48253                 tag: 'label',
48254                 'for':  id,
48255                 cls: 'control-label',
48256                 cn: []
48257             };
48258
48259             var label_text = {
48260                 tag: 'span',
48261                 html: this.fieldLabel
48262             };
48263
48264             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48265             label.cn = [
48266                 indicator,
48267                 label_text
48268             ];
48269
48270             if(this.indicatorpos == 'right') {
48271                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48272                 label.cn = [
48273                     label_text,
48274                     indicator
48275                 ];
48276             }
48277
48278             if(align == 'left') {
48279                 container = {
48280                     tag: 'div',
48281                     cn: [
48282                         container
48283                     ]
48284                 };
48285
48286                 if(this.labelWidth > 12){
48287                     label.style = "width: " + this.labelWidth + 'px';
48288                 }
48289                 if(this.labelWidth < 13 && this.labelmd == 0){
48290                     this.labelmd = this.labelWidth;
48291                 }
48292                 if(this.labellg > 0){
48293                     label.cls += ' col-lg-' + this.labellg;
48294                     input.cls += ' col-lg-' + (12 - this.labellg);
48295                 }
48296                 if(this.labelmd > 0){
48297                     label.cls += ' col-md-' + this.labelmd;
48298                     container.cls += ' col-md-' + (12 - this.labelmd);
48299                 }
48300                 if(this.labelsm > 0){
48301                     label.cls += ' col-sm-' + this.labelsm;
48302                     container.cls += ' col-sm-' + (12 - this.labelsm);
48303                 }
48304                 if(this.labelxs > 0){
48305                     label.cls += ' col-xs-' + this.labelxs;
48306                     container.cls += ' col-xs-' + (12 - this.labelxs);
48307                 }
48308             }
48309         }
48310
48311         cfg.cn = [
48312             label,
48313             container,
48314             hiddenInput
48315         ];
48316         
48317         var settings = this;
48318
48319         ['xs','sm','md','lg'].map(function(size){
48320             if (settings[size]) {
48321                 cfg.cls += ' col-' + size + '-' + settings[size];
48322             }
48323         });
48324         
48325         return cfg;
48326     },
48327     
48328     initEvents : function()
48329     {
48330         this.indicator = this.indicatorEl();
48331         
48332         this.initCurrencyEvent();
48333         
48334         this.initNumberEvent();
48335     },
48336     
48337     initCurrencyEvent : function()
48338     {
48339         if (!this.store) {
48340             throw "can not find store for combo";
48341         }
48342         
48343         this.store = Roo.factory(this.store, Roo.data);
48344         this.store.parent = this;
48345         
48346         this.createList();
48347         
48348         this.triggerEl = this.el.select('.input-group-addon', true).first();
48349         
48350         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48351         
48352         var _this = this;
48353         
48354         (function(){
48355             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48356             _this.list.setWidth(lw);
48357         }).defer(100);
48358         
48359         this.list.on('mouseover', this.onViewOver, this);
48360         this.list.on('mousemove', this.onViewMove, this);
48361         this.list.on('scroll', this.onViewScroll, this);
48362         
48363         if(!this.tpl){
48364             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48365         }
48366         
48367         this.view = new Roo.View(this.list, this.tpl, {
48368             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48369         });
48370         
48371         this.view.on('click', this.onViewClick, this);
48372         
48373         this.store.on('beforeload', this.onBeforeLoad, this);
48374         this.store.on('load', this.onLoad, this);
48375         this.store.on('loadexception', this.onLoadException, this);
48376         
48377         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48378             "up" : function(e){
48379                 this.inKeyMode = true;
48380                 this.selectPrev();
48381             },
48382
48383             "down" : function(e){
48384                 if(!this.isExpanded()){
48385                     this.onTriggerClick();
48386                 }else{
48387                     this.inKeyMode = true;
48388                     this.selectNext();
48389                 }
48390             },
48391
48392             "enter" : function(e){
48393                 this.collapse();
48394                 
48395                 if(this.fireEvent("specialkey", this, e)){
48396                     this.onViewClick(false);
48397                 }
48398                 
48399                 return true;
48400             },
48401
48402             "esc" : function(e){
48403                 this.collapse();
48404             },
48405
48406             "tab" : function(e){
48407                 this.collapse();
48408                 
48409                 if(this.fireEvent("specialkey", this, e)){
48410                     this.onViewClick(false);
48411                 }
48412                 
48413                 return true;
48414             },
48415
48416             scope : this,
48417
48418             doRelay : function(foo, bar, hname){
48419                 if(hname == 'down' || this.scope.isExpanded()){
48420                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48421                 }
48422                 return true;
48423             },
48424
48425             forceKeyDown: true
48426         });
48427         
48428         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48429         
48430     },
48431     
48432     initNumberEvent : function(e)
48433     {
48434         this.inputEl().on("keydown" , this.fireKey,  this);
48435         this.inputEl().on("focus", this.onFocus,  this);
48436         this.inputEl().on("blur", this.onBlur,  this);
48437         
48438         this.inputEl().relayEvent('keyup', this);
48439         
48440         if(this.indicator){
48441             this.indicator.addClass('invisible');
48442         }
48443  
48444         this.originalValue = this.getValue();
48445         
48446         if(this.validationEvent == 'keyup'){
48447             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48448             this.inputEl().on('keyup', this.filterValidation, this);
48449         }
48450         else if(this.validationEvent !== false){
48451             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48452         }
48453         
48454         if(this.selectOnFocus){
48455             this.on("focus", this.preFocus, this);
48456             
48457         }
48458         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48459             this.inputEl().on("keypress", this.filterKeys, this);
48460         } else {
48461             this.inputEl().relayEvent('keypress', this);
48462         }
48463         
48464         var allowed = "0123456789";
48465         
48466         if(this.allowDecimals){
48467             allowed += this.decimalSeparator;
48468         }
48469         
48470         if(this.allowNegative){
48471             allowed += "-";
48472         }
48473         
48474         if(this.thousandsDelimiter) {
48475             allowed += ",";
48476         }
48477         
48478         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48479         
48480         var keyPress = function(e){
48481             
48482             var k = e.getKey();
48483             
48484             var c = e.getCharCode();
48485             
48486             if(
48487                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48488                     allowed.indexOf(String.fromCharCode(c)) === -1
48489             ){
48490                 e.stopEvent();
48491                 return;
48492             }
48493             
48494             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48495                 return;
48496             }
48497             
48498             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48499                 e.stopEvent();
48500             }
48501         };
48502         
48503         this.inputEl().on("keypress", keyPress, this);
48504         
48505     },
48506     
48507     onTriggerClick : function(e)
48508     {   
48509         if(this.disabled){
48510             return;
48511         }
48512         
48513         this.page = 0;
48514         this.loadNext = false;
48515         
48516         if(this.isExpanded()){
48517             this.collapse();
48518             return;
48519         }
48520         
48521         this.hasFocus = true;
48522         
48523         if(this.triggerAction == 'all') {
48524             this.doQuery(this.allQuery, true);
48525             return;
48526         }
48527         
48528         this.doQuery(this.getRawValue());
48529     },
48530     
48531     getCurrency : function()
48532     {   
48533         var v = this.currencyEl().getValue();
48534         
48535         return v;
48536     },
48537     
48538     restrictHeight : function()
48539     {
48540         this.list.alignTo(this.currencyEl(), this.listAlign);
48541         this.list.alignTo(this.currencyEl(), this.listAlign);
48542     },
48543     
48544     onViewClick : function(view, doFocus, el, e)
48545     {
48546         var index = this.view.getSelectedIndexes()[0];
48547         
48548         var r = this.store.getAt(index);
48549         
48550         if(r){
48551             this.onSelect(r, index);
48552         }
48553     },
48554     
48555     onSelect : function(record, index){
48556         
48557         if(this.fireEvent('beforeselect', this, record, index) !== false){
48558         
48559             this.setFromCurrencyData(index > -1 ? record.data : false);
48560             
48561             this.collapse();
48562             
48563             this.fireEvent('select', this, record, index);
48564         }
48565     },
48566     
48567     setFromCurrencyData : function(o)
48568     {
48569         var currency = '';
48570         
48571         this.lastCurrency = o;
48572         
48573         if (this.currencyField) {
48574             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48575         } else {
48576             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48577         }
48578         
48579         this.lastSelectionText = currency;
48580         
48581         //setting default currency
48582         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48583             this.setCurrency(this.defaultCurrency);
48584             return;
48585         }
48586         
48587         this.setCurrency(currency);
48588     },
48589     
48590     setFromData : function(o)
48591     {
48592         var c = {};
48593         
48594         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48595         
48596         this.setFromCurrencyData(c);
48597         
48598         var value = '';
48599         
48600         if (this.name) {
48601             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48602         } else {
48603             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48604         }
48605         
48606         this.setValue(value);
48607         
48608     },
48609     
48610     setCurrency : function(v)
48611     {   
48612         this.currencyValue = v;
48613         
48614         if(this.rendered){
48615             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48616             this.validate();
48617         }
48618     },
48619     
48620     setValue : function(v)
48621     {
48622         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48623         
48624         this.value = v;
48625         
48626         if(this.rendered){
48627             
48628             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48629             
48630             this.inputEl().dom.value = (v == '') ? '' :
48631                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48632             
48633             if(!this.allowZero && v === '0') {
48634                 this.hiddenEl().dom.value = '';
48635                 this.inputEl().dom.value = '';
48636             }
48637             
48638             this.validate();
48639         }
48640     },
48641     
48642     getRawValue : function()
48643     {
48644         var v = this.inputEl().getValue();
48645         
48646         return v;
48647     },
48648     
48649     getValue : function()
48650     {
48651         return this.fixPrecision(this.parseValue(this.getRawValue()));
48652     },
48653     
48654     parseValue : function(value)
48655     {
48656         if(this.thousandsDelimiter) {
48657             value += "";
48658             r = new RegExp(",", "g");
48659             value = value.replace(r, "");
48660         }
48661         
48662         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48663         return isNaN(value) ? '' : value;
48664         
48665     },
48666     
48667     fixPrecision : function(value)
48668     {
48669         if(this.thousandsDelimiter) {
48670             value += "";
48671             r = new RegExp(",", "g");
48672             value = value.replace(r, "");
48673         }
48674         
48675         var nan = isNaN(value);
48676         
48677         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48678             return nan ? '' : value;
48679         }
48680         return parseFloat(value).toFixed(this.decimalPrecision);
48681     },
48682     
48683     decimalPrecisionFcn : function(v)
48684     {
48685         return Math.floor(v);
48686     },
48687     
48688     validateValue : function(value)
48689     {
48690         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48691             return false;
48692         }
48693         
48694         var num = this.parseValue(value);
48695         
48696         if(isNaN(num)){
48697             this.markInvalid(String.format(this.nanText, value));
48698             return false;
48699         }
48700         
48701         if(num < this.minValue){
48702             this.markInvalid(String.format(this.minText, this.minValue));
48703             return false;
48704         }
48705         
48706         if(num > this.maxValue){
48707             this.markInvalid(String.format(this.maxText, this.maxValue));
48708             return false;
48709         }
48710         
48711         return true;
48712     },
48713     
48714     validate : function()
48715     {
48716         if(this.disabled || this.allowBlank){
48717             this.markValid();
48718             return true;
48719         }
48720         
48721         var currency = this.getCurrency();
48722         
48723         if(this.validateValue(this.getRawValue()) && currency.length){
48724             this.markValid();
48725             return true;
48726         }
48727         
48728         this.markInvalid();
48729         return false;
48730     },
48731     
48732     getName: function()
48733     {
48734         return this.name;
48735     },
48736     
48737     beforeBlur : function()
48738     {
48739         if(!this.castInt){
48740             return;
48741         }
48742         
48743         var v = this.parseValue(this.getRawValue());
48744         
48745         if(v || v == 0){
48746             this.setValue(v);
48747         }
48748     },
48749     
48750     onBlur : function()
48751     {
48752         this.beforeBlur();
48753         
48754         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48755             //this.el.removeClass(this.focusClass);
48756         }
48757         
48758         this.hasFocus = false;
48759         
48760         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48761             this.validate();
48762         }
48763         
48764         var v = this.getValue();
48765         
48766         if(String(v) !== String(this.startValue)){
48767             this.fireEvent('change', this, v, this.startValue);
48768         }
48769         
48770         this.fireEvent("blur", this);
48771     },
48772     
48773     inputEl : function()
48774     {
48775         return this.el.select('.roo-money-amount-input', true).first();
48776     },
48777     
48778     currencyEl : function()
48779     {
48780         return this.el.select('.roo-money-currency-input', true).first();
48781     },
48782     
48783     hiddenEl : function()
48784     {
48785         return this.el.select('input.hidden-number-input',true).first();
48786     }
48787     
48788 });/**
48789  * @class Roo.bootstrap.BezierSignature
48790  * @extends Roo.bootstrap.Component
48791  * Bootstrap BezierSignature class
48792  * This script refer to:
48793  *    Title: Signature Pad
48794  *    Author: szimek
48795  *    Availability: https://github.com/szimek/signature_pad
48796  *
48797  * @constructor
48798  * Create a new BezierSignature
48799  * @param {Object} config The config object
48800  */
48801
48802 Roo.bootstrap.BezierSignature = function(config){
48803     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48804     this.addEvents({
48805         "resize" : true
48806     });
48807 };
48808
48809 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48810 {
48811      
48812     curve_data: [],
48813     
48814     is_empty: true,
48815     
48816     mouse_btn_down: true,
48817     
48818     /**
48819      * @cfg {int} canvas height
48820      */
48821     canvas_height: '200px',
48822     
48823     /**
48824      * @cfg {float|function} Radius of a single dot.
48825      */ 
48826     dot_size: false,
48827     
48828     /**
48829      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48830      */
48831     min_width: 0.5,
48832     
48833     /**
48834      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48835      */
48836     max_width: 2.5,
48837     
48838     /**
48839      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48840      */
48841     throttle: 16,
48842     
48843     /**
48844      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48845      */
48846     min_distance: 5,
48847     
48848     /**
48849      * @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.
48850      */
48851     bg_color: 'rgba(0, 0, 0, 0)',
48852     
48853     /**
48854      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48855      */
48856     dot_color: 'black',
48857     
48858     /**
48859      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48860      */ 
48861     velocity_filter_weight: 0.7,
48862     
48863     /**
48864      * @cfg {function} Callback when stroke begin. 
48865      */
48866     onBegin: false,
48867     
48868     /**
48869      * @cfg {function} Callback when stroke end.
48870      */
48871     onEnd: false,
48872     
48873     getAutoCreate : function()
48874     {
48875         var cls = 'roo-signature column';
48876         
48877         if(this.cls){
48878             cls += ' ' + this.cls;
48879         }
48880         
48881         var col_sizes = [
48882             'lg',
48883             'md',
48884             'sm',
48885             'xs'
48886         ];
48887         
48888         for(var i = 0; i < col_sizes.length; i++) {
48889             if(this[col_sizes[i]]) {
48890                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48891             }
48892         }
48893         
48894         var cfg = {
48895             tag: 'div',
48896             cls: cls,
48897             cn: [
48898                 {
48899                     tag: 'div',
48900                     cls: 'roo-signature-body',
48901                     cn: [
48902                         {
48903                             tag: 'canvas',
48904                             cls: 'roo-signature-body-canvas',
48905                             height: this.canvas_height,
48906                             width: this.canvas_width
48907                         }
48908                     ]
48909                 },
48910                 {
48911                     tag: 'input',
48912                     type: 'file',
48913                     style: 'display: none'
48914                 }
48915             ]
48916         };
48917         
48918         return cfg;
48919     },
48920     
48921     initEvents: function() 
48922     {
48923         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48924         
48925         var canvas = this.canvasEl();
48926         
48927         // mouse && touch event swapping...
48928         canvas.dom.style.touchAction = 'none';
48929         canvas.dom.style.msTouchAction = 'none';
48930         
48931         this.mouse_btn_down = false;
48932         canvas.on('mousedown', this._handleMouseDown, this);
48933         canvas.on('mousemove', this._handleMouseMove, this);
48934         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48935         
48936         if (window.PointerEvent) {
48937             canvas.on('pointerdown', this._handleMouseDown, this);
48938             canvas.on('pointermove', this._handleMouseMove, this);
48939             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48940         }
48941         
48942         if ('ontouchstart' in window) {
48943             canvas.on('touchstart', this._handleTouchStart, this);
48944             canvas.on('touchmove', this._handleTouchMove, this);
48945             canvas.on('touchend', this._handleTouchEnd, this);
48946         }
48947         
48948         Roo.EventManager.onWindowResize(this.resize, this, true);
48949         
48950         // file input event
48951         this.fileEl().on('change', this.uploadImage, this);
48952         
48953         this.clear();
48954         
48955         this.resize();
48956     },
48957     
48958     resize: function(){
48959         
48960         var canvas = this.canvasEl().dom;
48961         var ctx = this.canvasElCtx();
48962         var img_data = false;
48963         
48964         if(canvas.width > 0) {
48965             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48966         }
48967         // setting canvas width will clean img data
48968         canvas.width = 0;
48969         
48970         var style = window.getComputedStyle ? 
48971             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48972             
48973         var padding_left = parseInt(style.paddingLeft) || 0;
48974         var padding_right = parseInt(style.paddingRight) || 0;
48975         
48976         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48977         
48978         if(img_data) {
48979             ctx.putImageData(img_data, 0, 0);
48980         }
48981     },
48982     
48983     _handleMouseDown: function(e)
48984     {
48985         if (e.browserEvent.which === 1) {
48986             this.mouse_btn_down = true;
48987             this.strokeBegin(e);
48988         }
48989     },
48990     
48991     _handleMouseMove: function (e)
48992     {
48993         if (this.mouse_btn_down) {
48994             this.strokeMoveUpdate(e);
48995         }
48996     },
48997     
48998     _handleMouseUp: function (e)
48999     {
49000         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49001             this.mouse_btn_down = false;
49002             this.strokeEnd(e);
49003         }
49004     },
49005     
49006     _handleTouchStart: function (e) {
49007         
49008         e.preventDefault();
49009         if (e.browserEvent.targetTouches.length === 1) {
49010             // var touch = e.browserEvent.changedTouches[0];
49011             // this.strokeBegin(touch);
49012             
49013              this.strokeBegin(e); // assume e catching the correct xy...
49014         }
49015     },
49016     
49017     _handleTouchMove: function (e) {
49018         e.preventDefault();
49019         // var touch = event.targetTouches[0];
49020         // _this._strokeMoveUpdate(touch);
49021         this.strokeMoveUpdate(e);
49022     },
49023     
49024     _handleTouchEnd: function (e) {
49025         var wasCanvasTouched = e.target === this.canvasEl().dom;
49026         if (wasCanvasTouched) {
49027             e.preventDefault();
49028             // var touch = event.changedTouches[0];
49029             // _this._strokeEnd(touch);
49030             this.strokeEnd(e);
49031         }
49032     },
49033     
49034     reset: function () {
49035         this._lastPoints = [];
49036         this._lastVelocity = 0;
49037         this._lastWidth = (this.min_width + this.max_width) / 2;
49038         this.canvasElCtx().fillStyle = this.dot_color;
49039     },
49040     
49041     strokeMoveUpdate: function(e)
49042     {
49043         this.strokeUpdate(e);
49044         
49045         if (this.throttle) {
49046             this.throttleStroke(this.strokeUpdate, this.throttle);
49047         }
49048         else {
49049             this.strokeUpdate(e);
49050         }
49051     },
49052     
49053     strokeBegin: function(e)
49054     {
49055         var newPointGroup = {
49056             color: this.dot_color,
49057             points: []
49058         };
49059         
49060         if (typeof this.onBegin === 'function') {
49061             this.onBegin(e);
49062         }
49063         
49064         this.curve_data.push(newPointGroup);
49065         this.reset();
49066         this.strokeUpdate(e);
49067     },
49068     
49069     strokeUpdate: function(e)
49070     {
49071         var rect = this.canvasEl().dom.getBoundingClientRect();
49072         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49073         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49074         var lastPoints = lastPointGroup.points;
49075         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49076         var isLastPointTooClose = lastPoint
49077             ? point.distanceTo(lastPoint) <= this.min_distance
49078             : false;
49079         var color = lastPointGroup.color;
49080         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49081             var curve = this.addPoint(point);
49082             if (!lastPoint) {
49083                 this.drawDot({color: color, point: point});
49084             }
49085             else if (curve) {
49086                 this.drawCurve({color: color, curve: curve});
49087             }
49088             lastPoints.push({
49089                 time: point.time,
49090                 x: point.x,
49091                 y: point.y
49092             });
49093         }
49094     },
49095     
49096     strokeEnd: function(e)
49097     {
49098         this.strokeUpdate(e);
49099         if (typeof this.onEnd === 'function') {
49100             this.onEnd(e);
49101         }
49102     },
49103     
49104     addPoint:  function (point) {
49105         var _lastPoints = this._lastPoints;
49106         _lastPoints.push(point);
49107         if (_lastPoints.length > 2) {
49108             if (_lastPoints.length === 3) {
49109                 _lastPoints.unshift(_lastPoints[0]);
49110             }
49111             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49112             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49113             _lastPoints.shift();
49114             return curve;
49115         }
49116         return null;
49117     },
49118     
49119     calculateCurveWidths: function (startPoint, endPoint) {
49120         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49121             (1 - this.velocity_filter_weight) * this._lastVelocity;
49122
49123         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49124         var widths = {
49125             end: newWidth,
49126             start: this._lastWidth
49127         };
49128         
49129         this._lastVelocity = velocity;
49130         this._lastWidth = newWidth;
49131         return widths;
49132     },
49133     
49134     drawDot: function (_a) {
49135         var color = _a.color, point = _a.point;
49136         var ctx = this.canvasElCtx();
49137         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49138         ctx.beginPath();
49139         this.drawCurveSegment(point.x, point.y, width);
49140         ctx.closePath();
49141         ctx.fillStyle = color;
49142         ctx.fill();
49143     },
49144     
49145     drawCurve: function (_a) {
49146         var color = _a.color, curve = _a.curve;
49147         var ctx = this.canvasElCtx();
49148         var widthDelta = curve.endWidth - curve.startWidth;
49149         var drawSteps = Math.floor(curve.length()) * 2;
49150         ctx.beginPath();
49151         ctx.fillStyle = color;
49152         for (var i = 0; i < drawSteps; i += 1) {
49153         var t = i / drawSteps;
49154         var tt = t * t;
49155         var ttt = tt * t;
49156         var u = 1 - t;
49157         var uu = u * u;
49158         var uuu = uu * u;
49159         var x = uuu * curve.startPoint.x;
49160         x += 3 * uu * t * curve.control1.x;
49161         x += 3 * u * tt * curve.control2.x;
49162         x += ttt * curve.endPoint.x;
49163         var y = uuu * curve.startPoint.y;
49164         y += 3 * uu * t * curve.control1.y;
49165         y += 3 * u * tt * curve.control2.y;
49166         y += ttt * curve.endPoint.y;
49167         var width = curve.startWidth + ttt * widthDelta;
49168         this.drawCurveSegment(x, y, width);
49169         }
49170         ctx.closePath();
49171         ctx.fill();
49172     },
49173     
49174     drawCurveSegment: function (x, y, width) {
49175         var ctx = this.canvasElCtx();
49176         ctx.moveTo(x, y);
49177         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49178         this.is_empty = false;
49179     },
49180     
49181     clear: function()
49182     {
49183         var ctx = this.canvasElCtx();
49184         var canvas = this.canvasEl().dom;
49185         ctx.fillStyle = this.bg_color;
49186         ctx.clearRect(0, 0, canvas.width, canvas.height);
49187         ctx.fillRect(0, 0, canvas.width, canvas.height);
49188         this.curve_data = [];
49189         this.reset();
49190         this.is_empty = true;
49191     },
49192     
49193     fileEl: function()
49194     {
49195         return  this.el.select('input',true).first();
49196     },
49197     
49198     canvasEl: function()
49199     {
49200         return this.el.select('canvas',true).first();
49201     },
49202     
49203     canvasElCtx: function()
49204     {
49205         return this.el.select('canvas',true).first().dom.getContext('2d');
49206     },
49207     
49208     getImage: function(type)
49209     {
49210         if(this.is_empty) {
49211             return false;
49212         }
49213         
49214         // encryption ?
49215         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49216     },
49217     
49218     drawFromImage: function(img_src)
49219     {
49220         var img = new Image();
49221         
49222         img.onload = function(){
49223             this.canvasElCtx().drawImage(img, 0, 0);
49224         }.bind(this);
49225         
49226         img.src = img_src;
49227         
49228         this.is_empty = false;
49229     },
49230     
49231     selectImage: function()
49232     {
49233         this.fileEl().dom.click();
49234     },
49235     
49236     uploadImage: function(e)
49237     {
49238         var reader = new FileReader();
49239         
49240         reader.onload = function(e){
49241             var img = new Image();
49242             img.onload = function(){
49243                 this.reset();
49244                 this.canvasElCtx().drawImage(img, 0, 0);
49245             }.bind(this);
49246             img.src = e.target.result;
49247         }.bind(this);
49248         
49249         reader.readAsDataURL(e.target.files[0]);
49250     },
49251     
49252     // Bezier Point Constructor
49253     Point: (function () {
49254         function Point(x, y, time) {
49255             this.x = x;
49256             this.y = y;
49257             this.time = time || Date.now();
49258         }
49259         Point.prototype.distanceTo = function (start) {
49260             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49261         };
49262         Point.prototype.equals = function (other) {
49263             return this.x === other.x && this.y === other.y && this.time === other.time;
49264         };
49265         Point.prototype.velocityFrom = function (start) {
49266             return this.time !== start.time
49267             ? this.distanceTo(start) / (this.time - start.time)
49268             : 0;
49269         };
49270         return Point;
49271     }()),
49272     
49273     
49274     // Bezier Constructor
49275     Bezier: (function () {
49276         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49277             this.startPoint = startPoint;
49278             this.control2 = control2;
49279             this.control1 = control1;
49280             this.endPoint = endPoint;
49281             this.startWidth = startWidth;
49282             this.endWidth = endWidth;
49283         }
49284         Bezier.fromPoints = function (points, widths, scope) {
49285             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49286             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49287             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49288         };
49289         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49290             var dx1 = s1.x - s2.x;
49291             var dy1 = s1.y - s2.y;
49292             var dx2 = s2.x - s3.x;
49293             var dy2 = s2.y - s3.y;
49294             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49295             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49296             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49297             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49298             var dxm = m1.x - m2.x;
49299             var dym = m1.y - m2.y;
49300             var k = l2 / (l1 + l2);
49301             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49302             var tx = s2.x - cm.x;
49303             var ty = s2.y - cm.y;
49304             return {
49305                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49306                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49307             };
49308         };
49309         Bezier.prototype.length = function () {
49310             var steps = 10;
49311             var length = 0;
49312             var px;
49313             var py;
49314             for (var i = 0; i <= steps; i += 1) {
49315                 var t = i / steps;
49316                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49317                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49318                 if (i > 0) {
49319                     var xdiff = cx - px;
49320                     var ydiff = cy - py;
49321                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49322                 }
49323                 px = cx;
49324                 py = cy;
49325             }
49326             return length;
49327         };
49328         Bezier.prototype.point = function (t, start, c1, c2, end) {
49329             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49330             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49331             + (3.0 * c2 * (1.0 - t) * t * t)
49332             + (end * t * t * t);
49333         };
49334         return Bezier;
49335     }()),
49336     
49337     throttleStroke: function(fn, wait) {
49338       if (wait === void 0) { wait = 250; }
49339       var previous = 0;
49340       var timeout = null;
49341       var result;
49342       var storedContext;
49343       var storedArgs;
49344       var later = function () {
49345           previous = Date.now();
49346           timeout = null;
49347           result = fn.apply(storedContext, storedArgs);
49348           if (!timeout) {
49349               storedContext = null;
49350               storedArgs = [];
49351           }
49352       };
49353       return function wrapper() {
49354           var args = [];
49355           for (var _i = 0; _i < arguments.length; _i++) {
49356               args[_i] = arguments[_i];
49357           }
49358           var now = Date.now();
49359           var remaining = wait - (now - previous);
49360           storedContext = this;
49361           storedArgs = args;
49362           if (remaining <= 0 || remaining > wait) {
49363               if (timeout) {
49364                   clearTimeout(timeout);
49365                   timeout = null;
49366               }
49367               previous = now;
49368               result = fn.apply(storedContext, storedArgs);
49369               if (!timeout) {
49370                   storedContext = null;
49371                   storedArgs = [];
49372               }
49373           }
49374           else if (!timeout) {
49375               timeout = window.setTimeout(later, remaining);
49376           }
49377           return result;
49378       };
49379   }
49380   
49381 });
49382
49383  
49384
49385  // old names for form elements
49386 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49387 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49388 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49389 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49390 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49391 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49392 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49393 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49394 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49395 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49396 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49397 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49398 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49399 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49400 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49401 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49402 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49403 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49404 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49405 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49406 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49407 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49408 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49409 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49410 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49411 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49412
49413 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49414 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49415
49416 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49417 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49418
49419 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49420 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49421 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49422 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49423