5b7a16256339fa51db1be1b3884e1c6661fbf097
[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){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             if(!this.footerShow && this.summaryFooterShow) {
9426                 cfg.cn.push(this.renderSummaryFooter());
9427             }
9428
9429             // where does this come from?
9430             //cfg.cls+=  ' TableGrid';
9431         }
9432         
9433         return { cn : [ cfg ] };
9434     },
9435     
9436     initEvents : function()
9437     {   
9438         if(!this.store || !this.cm){
9439             return;
9440         }
9441         if (this.selModel) {
9442             this.selModel.initEvents();
9443         }
9444         
9445         
9446         //Roo.log('initEvents with ds!!!!');
9447         
9448         this.bodyEl = this.el.select('tbody', true).first();
9449         this.headEl = this.el.select('thead', true).first();
9450         this.mainFoot = this.el.select('tfoot', true).first();
9451         
9452         
9453         
9454         
9455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9456             e.on('click', this.sort, this);
9457         }, this);
9458         
9459         
9460         // why is this done????? = it breaks dialogs??
9461         //this.parent().el.setStyle('position', 'relative');
9462         
9463         
9464         if (this.footer) {
9465             this.footer.parentId = this.id;
9466             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9467             
9468             if(this.lazyLoad){
9469                 this.el.select('tfoot tr td').first().addClass('hide');
9470             }
9471         } 
9472         
9473         if(this.loadMask) {
9474             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9475         }
9476         
9477         this.store.on('load', this.onLoad, this);
9478         this.store.on('beforeload', this.onBeforeLoad, this);
9479         this.store.on('update', this.onUpdate, this);
9480         this.store.on('add', this.onAdd, this);
9481         this.store.on("clear", this.clear, this);
9482         
9483         this.el.on("contextmenu", this.onContextMenu, this);
9484         
9485         
9486         this.cm.on("headerchange", this.onHeaderChange, this);
9487         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9488
9489  //?? does bodyEl get replaced on render?
9490         this.bodyEl.on("click", this.onClick, this);
9491         this.bodyEl.on("dblclick", this.onDblClick, this);        
9492         this.bodyEl.on('scroll', this.onBodyScroll, this);
9493
9494         // guessing mainbody will work - this relays usually caught by selmodel at present.
9495         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9496   
9497   
9498         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9499         
9500   
9501         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9502             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9503         }
9504         
9505         this.initCSS();
9506     },
9507     // Compatibility with grid - we implement all the view features at present.
9508     getView : function()
9509     {
9510         return this;
9511     },
9512     
9513     initCSS : function()
9514     {
9515         if(this.disableAutoSize) {
9516             return;
9517         }
9518         
9519         var cm = this.cm, styles = [];
9520         this.CSS.removeStyleSheet(this.id + '-cssrules');
9521         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9522         // we can honour xs/sm/md/xl  as widths...
9523         // we first have to decide what widht we are currently at...
9524         var sz = Roo.getGridSize();
9525         
9526         var total = 0;
9527         var last = -1;
9528         var cols = []; // visable cols.
9529         var total_abs = 0;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             var w = cm.getColumnWidth(i, false);
9532             if(cm.isHidden(i)){
9533                 cols.push( { rel : false, abs : 0 });
9534                 continue;
9535             }
9536             if (w !== false) {
9537                 cols.push( { rel : false, abs : w });
9538                 total_abs += w;
9539                 last = i; // not really..
9540                 continue;
9541             }
9542             var w = cm.getColumnWidth(i, sz);
9543             if (w > 0) {
9544                 last = i
9545             }
9546             total += w;
9547             cols.push( { rel : w, abs : false });
9548         }
9549         
9550         var avail = this.bodyEl.dom.clientWidth - total_abs;
9551         
9552         var unitWidth = Math.floor(avail / total);
9553         var rem = avail - (unitWidth * total);
9554         
9555         var hidden, width, pos = 0 , splithide , left;
9556         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9557             
9558             hidden = 'display:none;';
9559             left = '';
9560             width  = 'width:0px;';
9561             splithide = '';
9562             if(!cm.isHidden(i)){
9563                 hidden = '';
9564                 
9565                 
9566                 // we can honour xs/sm/md/xl ?
9567                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9568                 if (w===0) {
9569                     hidden = 'display:none;';
9570                 }
9571                 // width should return a small number...
9572                 if (i == last) {
9573                     w+=rem; // add the remaining with..
9574                 }
9575                 pos += w;
9576                 left = "left:" + (pos -4) + "px;";
9577                 width = "width:" + w+ "px;";
9578                 
9579             }
9580             if (this.responsive) {
9581                 width = '';
9582                 left = '';
9583                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9584                 splithide = 'display: none;';
9585             }
9586             
9587             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9588             if (this.headEl) {
9589                 if (i == last) {
9590                     splithide = 'display:none;';
9591                 }
9592                 
9593                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9594                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9595                             // this is the popover version..
9596                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9597                 );
9598             }
9599             
9600         }
9601         //Roo.log(styles.join(''));
9602         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9603         
9604     },
9605     
9606     
9607     
9608     onContextMenu : function(e, t)
9609     {
9610         this.processEvent("contextmenu", e);
9611     },
9612     
9613     processEvent : function(name, e)
9614     {
9615         if (name != 'touchstart' ) {
9616             this.fireEvent(name, e);    
9617         }
9618         
9619         var t = e.getTarget();
9620         
9621         var cell = Roo.get(t);
9622         
9623         if(!cell){
9624             return;
9625         }
9626         
9627         if(cell.findParent('tfoot', false, true)){
9628             return;
9629         }
9630         
9631         if(cell.findParent('thead', false, true)){
9632             
9633             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9634                 cell = Roo.get(t).findParent('th', false, true);
9635                 if (!cell) {
9636                     Roo.log("failed to find th in thead?");
9637                     Roo.log(e.getTarget());
9638                     return;
9639                 }
9640             }
9641             
9642             var cellIndex = cell.dom.cellIndex;
9643             
9644             var ename = name == 'touchstart' ? 'click' : name;
9645             this.fireEvent("header" + ename, this, cellIndex, e);
9646             
9647             return;
9648         }
9649         
9650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9651             cell = Roo.get(t).findParent('td', false, true);
9652             if (!cell) {
9653                 Roo.log("failed to find th in tbody?");
9654                 Roo.log(e.getTarget());
9655                 return;
9656             }
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1;
9662         
9663         if(row !== false){
9664             
9665             this.fireEvent("row" + name, this, rowIndex, e);
9666             
9667             if(cell !== false){
9668             
9669                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9670             }
9671         }
9672         
9673     },
9674     
9675     onMouseover : function(e, el)
9676     {
9677         var cell = Roo.get(el);
9678         
9679         if(!cell){
9680             return;
9681         }
9682         
9683         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9684             cell = cell.findParent('td', false, true);
9685         }
9686         
9687         var row = cell.findParent('tr', false, true);
9688         var cellIndex = cell.dom.cellIndex;
9689         var rowIndex = row.dom.rowIndex - 1; // start from 0
9690         
9691         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9692         
9693     },
9694     
9695     onMouseout : function(e, el)
9696     {
9697         var cell = Roo.get(el);
9698         
9699         if(!cell){
9700             return;
9701         }
9702         
9703         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9704             cell = cell.findParent('td', false, true);
9705         }
9706         
9707         var row = cell.findParent('tr', false, true);
9708         var cellIndex = cell.dom.cellIndex;
9709         var rowIndex = row.dom.rowIndex - 1; // start from 0
9710         
9711         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9712         
9713     },
9714     
9715     onClick : function(e, el)
9716     {
9717         var cell = Roo.get(el);
9718         
9719         if(!cell || (!this.cellSelection && !this.rowSelection)){
9720             return;
9721         }
9722         
9723         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9724             cell = cell.findParent('td', false, true);
9725         }
9726         
9727         if(!cell || typeof(cell) == 'undefined'){
9728             return;
9729         }
9730         
9731         var row = cell.findParent('tr', false, true);
9732         
9733         if(!row || typeof(row) == 'undefined'){
9734             return;
9735         }
9736         
9737         var cellIndex = cell.dom.cellIndex;
9738         var rowIndex = this.getRowIndex(row);
9739         
9740         // why??? - should these not be based on SelectionModel?
9741         //if(this.cellSelection){
9742             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9743         //}
9744         
9745         //if(this.rowSelection){
9746             this.fireEvent('rowclick', this, row, rowIndex, e);
9747         //}
9748          
9749     },
9750         
9751     onDblClick : function(e,el)
9752     {
9753         var cell = Roo.get(el);
9754         
9755         if(!cell || (!this.cellSelection && !this.rowSelection)){
9756             return;
9757         }
9758         
9759         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9760             cell = cell.findParent('td', false, true);
9761         }
9762         
9763         if(!cell || typeof(cell) == 'undefined'){
9764             return;
9765         }
9766         
9767         var row = cell.findParent('tr', false, true);
9768         
9769         if(!row || typeof(row) == 'undefined'){
9770             return;
9771         }
9772         
9773         var cellIndex = cell.dom.cellIndex;
9774         var rowIndex = this.getRowIndex(row);
9775         
9776         if(this.cellSelection){
9777             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9778         }
9779         
9780         if(this.rowSelection){
9781             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9782         }
9783     },
9784     findRowIndex : function(el)
9785     {
9786         var cell = Roo.get(el);
9787         if(!cell) {
9788             return false;
9789         }
9790         var row = cell.findParent('tr', false, true);
9791         
9792         if(!row || typeof(row) == 'undefined'){
9793             return false;
9794         }
9795         return this.getRowIndex(row);
9796     },
9797     sort : function(e,el)
9798     {
9799         var col = Roo.get(el);
9800         
9801         if(!col.hasClass('sortable')){
9802             return;
9803         }
9804         
9805         var sort = col.attr('sort');
9806         var dir = 'ASC';
9807         
9808         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9809             dir = 'DESC';
9810         }
9811         
9812         this.store.sortInfo = {field : sort, direction : dir};
9813         
9814         if (this.footer) {
9815             Roo.log("calling footer first");
9816             this.footer.onClick('first');
9817         } else {
9818         
9819             this.store.load({ params : { start : 0 } });
9820         }
9821     },
9822     
9823     renderHeader : function()
9824     {
9825         var header = {
9826             tag: 'thead',
9827             cn : []
9828         };
9829         
9830         var cm = this.cm;
9831         this.totalWidth = 0;
9832         
9833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9834             
9835             var config = cm.config[i];
9836             
9837             var c = {
9838                 tag: 'th',
9839                 cls : 'x-hcol-' + i,
9840                 style : '',
9841                 
9842                 html: cm.getColumnHeader(i)
9843             };
9844             
9845             var tooltip = cm.getColumnTooltip(i);
9846             if (tooltip) {
9847                 c.tooltip = tooltip;
9848             }
9849             
9850             
9851             var hh = '';
9852             
9853             if(typeof(config.sortable) != 'undefined' && config.sortable){
9854                 c.cls += ' sortable';
9855                 c.html = '<i class="fa"></i>' + c.html;
9856             }
9857             
9858             // could use BS4 hidden-..-down 
9859             
9860             if(typeof(config.lgHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.mdHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.smHeader) != 'undefined'){
9869                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9870             }
9871             
9872             if(typeof(config.xsHeader) != 'undefined'){
9873                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9874             }
9875             
9876             if(hh.length){
9877                 c.html = hh;
9878             }
9879             
9880             if(typeof(config.tooltip) != 'undefined'){
9881                 c.tooltip = config.tooltip;
9882             }
9883             
9884             if(typeof(config.colspan) != 'undefined'){
9885                 c.colspan = config.colspan;
9886             }
9887             
9888             // hidden is handled by CSS now
9889             
9890             if(typeof(config.dataIndex) != 'undefined'){
9891                 c.sort = config.dataIndex;
9892             }
9893             
9894            
9895             
9896             if(typeof(config.align) != 'undefined' && config.align.length){
9897                 c.style += ' text-align:' + config.align + ';';
9898             }
9899             
9900             /* width is done in CSS
9901              *if(typeof(config.width) != 'undefined'){
9902                 c.style += ' width:' + config.width + 'px;';
9903                 this.totalWidth += config.width;
9904             } else {
9905                 this.totalWidth += 100; // assume minimum of 100 per column?
9906             }
9907             */
9908             
9909             if(typeof(config.cls) != 'undefined'){
9910                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9911             }
9912             // this is the bit that doesnt reall work at all...
9913             
9914             if (this.responsive) {
9915                  
9916             
9917                 ['xs','sm','md','lg'].map(function(size){
9918                     
9919                     if(typeof(config[size]) == 'undefined'){
9920                         return;
9921                     }
9922                      
9923                     if (!config[size]) { // 0 = hidden
9924                         // BS 4 '0' is treated as hide that column and below.
9925                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9926                         return;
9927                     }
9928                     
9929                     c.cls += ' col-' + size + '-' + config[size] + (
9930                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9931                     );
9932                     
9933                     
9934                 });
9935             }
9936             // at the end?
9937             
9938             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9939             
9940             
9941             
9942             
9943             header.cn.push(c)
9944         }
9945         
9946         return header;
9947     },
9948     
9949     renderBody : function()
9950     {
9951         var body = {
9952             tag: 'tbody',
9953             cn : [
9954                 {
9955                     tag: 'tr',
9956                     cn : [
9957                         {
9958                             tag : 'td',
9959                             colspan :  this.cm.getColumnCount()
9960                         }
9961                     ]
9962                 }
9963             ]
9964         };
9965         
9966         return body;
9967     },
9968     
9969     renderFooter : function()
9970     {
9971         var footer = {
9972             tag: 'tfoot',
9973             cn : [
9974                 {
9975                     tag: 'tr',
9976                     cn : [
9977                         {
9978                             tag : 'td',
9979                             colspan :  this.cm.getColumnCount()
9980                         }
9981                     ]
9982                 }
9983             ]
9984         };
9985         
9986         return footer;
9987     },
9988
9989     renderSummaryFooter : function()
9990     {
9991         var footer = {
9992             tag: 'tfoot',
9993             cn : []
9994         };
9995
9996         var cm = this.cm;
9997         
9998         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9999             
10000             var c = {
10001                 tag: 'td',
10002                 cls : 'x-fcol-' + i,
10003                 style : '',
10004                 html: ''
10005             };
10006             
10007             footer.cn.push(c)
10008         }
10009         
10010         return footer;
10011     },
10012     
10013     
10014     
10015     onLoad : function()
10016     {
10017 //        Roo.log('ds onload');
10018         this.clear();
10019         
10020         var _this = this;
10021         var cm = this.cm;
10022         var ds = this.store;
10023         
10024         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10025             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10026             if (_this.store.sortInfo) {
10027                     
10028                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10029                     e.select('i', true).addClass(['fa-arrow-up']);
10030                 }
10031                 
10032                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10033                     e.select('i', true).addClass(['fa-arrow-down']);
10034                 }
10035             }
10036         });
10037         
10038         var tbody =  this.bodyEl;
10039               
10040         if(ds.getCount() > 0){
10041             ds.data.each(function(d,rowIndex){
10042                 var row =  this.renderRow(cm, ds, rowIndex);
10043
10044                 Roo.log("ROW");
10045                 Roo.log(row);
10046                 
10047                 tbody.createChild(row);
10048                 
10049                 var _this = this;
10050                 
10051                 if(row.cellObjects.length){
10052                     Roo.each(row.cellObjects, function(r){
10053                         _this.renderCellObject(r);
10054                     })
10055                 }
10056                 
10057             }, this);
10058         } else if (this.empty_results.length) {
10059             this.el.mask(this.empty_results, 'no-spinner');
10060         }
10061         
10062         var tfoot = this.el.select('tfoot', true).first();
10063         
10064         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10065             
10066             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10067             
10068             var total = this.ds.getTotalCount();
10069             
10070             if(this.footer.pageSize < total){
10071                 this.mainFoot.show();
10072             }
10073         }
10074
10075         if(!this.footerShow && this.summaryFooterShow) {
10076
10077             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10078         
10079                 var value = cm.config[i].summaryFooter;
10080
10081                 Roo.log('value [' + i + '] : ' + value);
10082             }
10083         }
10084         
10085         Roo.each(this.el.select('tbody td', true).elements, function(e){
10086             e.on('mouseover', _this.onMouseover, _this);
10087         });
10088         
10089         Roo.each(this.el.select('tbody td', true).elements, function(e){
10090             e.on('mouseout', _this.onMouseout, _this);
10091         });
10092         this.fireEvent('rowsrendered', this);
10093         
10094         this.autoSize();
10095         
10096         this.initCSS(); /// resize cols
10097
10098         
10099     },
10100     
10101     
10102     onUpdate : function(ds,record)
10103     {
10104         this.refreshRow(record);
10105         this.autoSize();
10106     },
10107     
10108     onRemove : function(ds, record, index, isUpdate){
10109         if(isUpdate !== true){
10110             this.fireEvent("beforerowremoved", this, index, record);
10111         }
10112         var bt = this.bodyEl.dom;
10113         
10114         var rows = this.el.select('tbody > tr', true).elements;
10115         
10116         if(typeof(rows[index]) != 'undefined'){
10117             bt.removeChild(rows[index].dom);
10118         }
10119         
10120 //        if(bt.rows[index]){
10121 //            bt.removeChild(bt.rows[index]);
10122 //        }
10123         
10124         if(isUpdate !== true){
10125             //this.stripeRows(index);
10126             //this.syncRowHeights(index, index);
10127             //this.layout();
10128             this.fireEvent("rowremoved", this, index, record);
10129         }
10130     },
10131     
10132     onAdd : function(ds, records, rowIndex)
10133     {
10134         //Roo.log('on Add called');
10135         // - note this does not handle multiple adding very well..
10136         var bt = this.bodyEl.dom;
10137         for (var i =0 ; i < records.length;i++) {
10138             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10139             //Roo.log(records[i]);
10140             //Roo.log(this.store.getAt(rowIndex+i));
10141             this.insertRow(this.store, rowIndex + i, false);
10142             return;
10143         }
10144         
10145     },
10146     
10147     
10148     refreshRow : function(record){
10149         var ds = this.store, index;
10150         if(typeof record == 'number'){
10151             index = record;
10152             record = ds.getAt(index);
10153         }else{
10154             index = ds.indexOf(record);
10155             if (index < 0) {
10156                 return; // should not happen - but seems to 
10157             }
10158         }
10159         this.insertRow(ds, index, true);
10160         this.autoSize();
10161         this.onRemove(ds, record, index+1, true);
10162         this.autoSize();
10163         //this.syncRowHeights(index, index);
10164         //this.layout();
10165         this.fireEvent("rowupdated", this, index, record);
10166     },
10167     // private - called by RowSelection
10168     onRowSelect : function(rowIndex){
10169         var row = this.getRowDom(rowIndex);
10170         row.addClass(['bg-info','info']);
10171     },
10172     // private - called by RowSelection
10173     onRowDeselect : function(rowIndex)
10174     {
10175         if (rowIndex < 0) {
10176             return;
10177         }
10178         var row = this.getRowDom(rowIndex);
10179         row.removeClass(['bg-info','info']);
10180     },
10181       /**
10182      * Focuses the specified row.
10183      * @param {Number} row The row index
10184      */
10185     focusRow : function(row)
10186     {
10187         //Roo.log('GridView.focusRow');
10188         var x = this.bodyEl.dom.scrollLeft;
10189         this.focusCell(row, 0, false);
10190         this.bodyEl.dom.scrollLeft = x;
10191
10192     },
10193      /**
10194      * Focuses the specified cell.
10195      * @param {Number} row The row index
10196      * @param {Number} col The column index
10197      * @param {Boolean} hscroll false to disable horizontal scrolling
10198      */
10199     focusCell : function(row, col, hscroll)
10200     {
10201         //Roo.log('GridView.focusCell');
10202         var el = this.ensureVisible(row, col, hscroll);
10203         // not sure what focusEL achives = it's a <a> pos relative 
10204         //this.focusEl.alignTo(el, "tl-tl");
10205         //if(Roo.isGecko){
10206         //    this.focusEl.focus();
10207         //}else{
10208         //    this.focusEl.focus.defer(1, this.focusEl);
10209         //}
10210     },
10211     
10212      /**
10213      * Scrolls the specified cell into view
10214      * @param {Number} row The row index
10215      * @param {Number} col The column index
10216      * @param {Boolean} hscroll false to disable horizontal scrolling
10217      */
10218     ensureVisible : function(row, col, hscroll)
10219     {
10220         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10221         //return null; //disable for testing.
10222         if(typeof row != "number"){
10223             row = row.rowIndex;
10224         }
10225         if(row < 0 && row >= this.ds.getCount()){
10226             return  null;
10227         }
10228         col = (col !== undefined ? col : 0);
10229         var cm = this.cm;
10230         while(cm.isHidden(col)){
10231             col++;
10232         }
10233
10234         var el = this.getCellDom(row, col);
10235         if(!el){
10236             return null;
10237         }
10238         var c = this.bodyEl.dom;
10239
10240         var ctop = parseInt(el.offsetTop, 10);
10241         var cleft = parseInt(el.offsetLeft, 10);
10242         var cbot = ctop + el.offsetHeight;
10243         var cright = cleft + el.offsetWidth;
10244
10245         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10246         var ch = 0; //?? header is not withing the area?
10247         var stop = parseInt(c.scrollTop, 10);
10248         var sleft = parseInt(c.scrollLeft, 10);
10249         var sbot = stop + ch;
10250         var sright = sleft + c.clientWidth;
10251         /*
10252         Roo.log('GridView.ensureVisible:' +
10253                 ' ctop:' + ctop +
10254                 ' c.clientHeight:' + c.clientHeight +
10255                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10256                 ' stop:' + stop +
10257                 ' cbot:' + cbot +
10258                 ' sbot:' + sbot +
10259                 ' ch:' + ch  
10260                 );
10261         */
10262         if(ctop < stop){
10263             c.scrollTop = ctop;
10264             //Roo.log("set scrolltop to ctop DISABLE?");
10265         }else if(cbot > sbot){
10266             //Roo.log("set scrolltop to cbot-ch");
10267             c.scrollTop = cbot-ch;
10268         }
10269
10270         if(hscroll !== false){
10271             if(cleft < sleft){
10272                 c.scrollLeft = cleft;
10273             }else if(cright > sright){
10274                 c.scrollLeft = cright-c.clientWidth;
10275             }
10276         }
10277
10278         return el;
10279     },
10280     
10281     
10282     insertRow : function(dm, rowIndex, isUpdate){
10283         
10284         if(!isUpdate){
10285             this.fireEvent("beforerowsinserted", this, rowIndex);
10286         }
10287             //var s = this.getScrollState();
10288         var row = this.renderRow(this.cm, this.store, rowIndex);
10289         // insert before rowIndex..
10290         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10291         
10292         var _this = this;
10293                 
10294         if(row.cellObjects.length){
10295             Roo.each(row.cellObjects, function(r){
10296                 _this.renderCellObject(r);
10297             })
10298         }
10299             
10300         if(!isUpdate){
10301             this.fireEvent("rowsinserted", this, rowIndex);
10302             //this.syncRowHeights(firstRow, lastRow);
10303             //this.stripeRows(firstRow);
10304             //this.layout();
10305         }
10306         
10307     },
10308     
10309     
10310     getRowDom : function(rowIndex)
10311     {
10312         var rows = this.el.select('tbody > tr', true).elements;
10313         
10314         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10315         
10316     },
10317     getCellDom : function(rowIndex, colIndex)
10318     {
10319         var row = this.getRowDom(rowIndex);
10320         if (row === false) {
10321             return false;
10322         }
10323         var cols = row.select('td', true).elements;
10324         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10325         
10326     },
10327     
10328     // returns the object tree for a tr..
10329   
10330     
10331     renderRow : function(cm, ds, rowIndex) 
10332     {
10333         var d = ds.getAt(rowIndex);
10334         
10335         var row = {
10336             tag : 'tr',
10337             cls : 'x-row-' + rowIndex,
10338             cn : []
10339         };
10340             
10341         var cellObjects = [];
10342         
10343         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10344             var config = cm.config[i];
10345             
10346             var renderer = cm.getRenderer(i);
10347             var value = '';
10348             var id = false;
10349             
10350             if(typeof(renderer) !== 'undefined'){
10351                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10352             }
10353             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10354             // and are rendered into the cells after the row is rendered - using the id for the element.
10355             
10356             if(typeof(value) === 'object'){
10357                 id = Roo.id();
10358                 cellObjects.push({
10359                     container : id,
10360                     cfg : value 
10361                 })
10362             }
10363             
10364             var rowcfg = {
10365                 record: d,
10366                 rowIndex : rowIndex,
10367                 colIndex : i,
10368                 rowClass : ''
10369             };
10370
10371             this.fireEvent('rowclass', this, rowcfg);
10372             
10373             var td = {
10374                 tag: 'td',
10375                 // this might end up displaying HTML?
10376                 // this is too messy... - better to only do it on columsn you know are going to be too long
10377                 //tooltip : (typeof(value) === 'object') ? '' : value,
10378                 cls : rowcfg.rowClass + ' x-col-' + i,
10379                 style: '',
10380                 html: (typeof(value) === 'object') ? '' : value
10381             };
10382             
10383             if (id) {
10384                 td.id = id;
10385             }
10386             
10387             if(typeof(config.colspan) != 'undefined'){
10388                 td.colspan = config.colspan;
10389             }
10390             
10391             
10392             
10393             if(typeof(config.align) != 'undefined' && config.align.length){
10394                 td.style += ' text-align:' + config.align + ';';
10395             }
10396             if(typeof(config.valign) != 'undefined' && config.valign.length){
10397                 td.style += ' vertical-align:' + config.valign + ';';
10398             }
10399             /*
10400             if(typeof(config.width) != 'undefined'){
10401                 td.style += ' width:' +  config.width + 'px;';
10402             }
10403             */
10404             
10405             if(typeof(config.cursor) != 'undefined'){
10406                 td.style += ' cursor:' +  config.cursor + ';';
10407             }
10408             
10409             if(typeof(config.cls) != 'undefined'){
10410                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10411             }
10412             if (this.responsive) {
10413                 ['xs','sm','md','lg'].map(function(size){
10414                     
10415                     if(typeof(config[size]) == 'undefined'){
10416                         return;
10417                     }
10418                     
10419                     
10420                       
10421                     if (!config[size]) { // 0 = hidden
10422                         // BS 4 '0' is treated as hide that column and below.
10423                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10424                         return;
10425                     }
10426                     
10427                     td.cls += ' col-' + size + '-' + config[size] + (
10428                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10429                     );
10430                      
10431     
10432                 });
10433             }
10434             row.cn.push(td);
10435            
10436         }
10437         
10438         row.cellObjects = cellObjects;
10439         
10440         return row;
10441           
10442     },
10443     
10444     
10445     
10446     onBeforeLoad : function()
10447     {
10448         this.el.unmask(); // if needed.
10449     },
10450      /**
10451      * Remove all rows
10452      */
10453     clear : function()
10454     {
10455         this.el.select('tbody', true).first().dom.innerHTML = '';
10456     },
10457     /**
10458      * Show or hide a row.
10459      * @param {Number} rowIndex to show or hide
10460      * @param {Boolean} state hide
10461      */
10462     setRowVisibility : function(rowIndex, state)
10463     {
10464         var bt = this.bodyEl.dom;
10465         
10466         var rows = this.el.select('tbody > tr', true).elements;
10467         
10468         if(typeof(rows[rowIndex]) == 'undefined'){
10469             return;
10470         }
10471         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10472         
10473     },
10474     
10475     
10476     getSelectionModel : function(){
10477         if(!this.selModel){
10478             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10479         }
10480         return this.selModel;
10481     },
10482     /*
10483      * Render the Roo.bootstrap object from renderder
10484      */
10485     renderCellObject : function(r)
10486     {
10487         var _this = this;
10488         
10489         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10490         
10491         var t = r.cfg.render(r.container);
10492         
10493         if(r.cfg.cn){
10494             Roo.each(r.cfg.cn, function(c){
10495                 var child = {
10496                     container: t.getChildContainer(),
10497                     cfg: c
10498                 };
10499                 _this.renderCellObject(child);
10500             })
10501         }
10502     },
10503     /**
10504      * get the Row Index from a dom element.
10505      * @param {Roo.Element} row The row to look for
10506      * @returns {Number} the row
10507      */
10508     getRowIndex : function(row)
10509     {
10510         var rowIndex = -1;
10511         
10512         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10513             if(el != row){
10514                 return;
10515             }
10516             
10517             rowIndex = index;
10518         });
10519         
10520         return rowIndex;
10521     },
10522     /**
10523      * get the header TH element for columnIndex
10524      * @param {Number} columnIndex
10525      * @returns {Roo.Element}
10526      */
10527     getHeaderIndex: function(colIndex)
10528     {
10529         var cols = this.headEl.select('th', true).elements;
10530         return cols[colIndex]; 
10531     },
10532     /**
10533      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10534      * @param {domElement} cell to look for
10535      * @returns {Number} the column
10536      */
10537     getCellIndex : function(cell)
10538     {
10539         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10540         if(id){
10541             return parseInt(id[1], 10);
10542         }
10543         return 0;
10544     },
10545      /**
10546      * Returns the grid's underlying element = used by panel.Grid
10547      * @return {Element} The element
10548      */
10549     getGridEl : function(){
10550         return this.el;
10551     },
10552      /**
10553      * Forces a resize - used by panel.Grid
10554      * @return {Element} The element
10555      */
10556     autoSize : function()
10557     {
10558         if(this.disableAutoSize) {
10559             return;
10560         }
10561         //var ctr = Roo.get(this.container.dom.parentElement);
10562         var ctr = Roo.get(this.el.dom);
10563         
10564         var thd = this.getGridEl().select('thead',true).first();
10565         var tbd = this.getGridEl().select('tbody', true).first();
10566         var tfd = this.getGridEl().select('tfoot', true).first();
10567         
10568         var cw = ctr.getWidth();
10569         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10570         
10571         if (tbd) {
10572             
10573             tbd.setWidth(ctr.getWidth());
10574             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10575             // this needs fixing for various usage - currently only hydra job advers I think..
10576             //tdb.setHeight(
10577             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10578             //); 
10579             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10580             cw -= barsize;
10581         }
10582         cw = Math.max(cw, this.totalWidth);
10583         this.getGridEl().select('tbody tr',true).setWidth(cw);
10584         this.initCSS();
10585         
10586         // resize 'expandable coloumn?
10587         
10588         return; // we doe not have a view in this design..
10589         
10590     },
10591     onBodyScroll: function()
10592     {
10593         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10594         if(this.headEl){
10595             this.headEl.setStyle({
10596                 'position' : 'relative',
10597                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10598             });
10599         }
10600         
10601         if(this.lazyLoad){
10602             
10603             var scrollHeight = this.bodyEl.dom.scrollHeight;
10604             
10605             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10606             
10607             var height = this.bodyEl.getHeight();
10608             
10609             if(scrollHeight - height == scrollTop) {
10610                 
10611                 var total = this.ds.getTotalCount();
10612                 
10613                 if(this.footer.cursor + this.footer.pageSize < total){
10614                     
10615                     this.footer.ds.load({
10616                         params : {
10617                             start : this.footer.cursor + this.footer.pageSize,
10618                             limit : this.footer.pageSize
10619                         },
10620                         add : true
10621                     });
10622                 }
10623             }
10624             
10625         }
10626     },
10627     onColumnSplitterMoved : function(i, diff)
10628     {
10629         this.userResized = true;
10630         
10631         var cm = this.colModel;
10632         
10633         var w = this.getHeaderIndex(i).getWidth() + diff;
10634         
10635         
10636         cm.setColumnWidth(i, w, true);
10637         this.initCSS();
10638         //var cid = cm.getColumnId(i); << not used in this version?
10639        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10640         
10641         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10642         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10643         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10644 */
10645         //this.updateSplitters();
10646         //this.layout(); << ??
10647         this.fireEvent("columnresize", i, w);
10648     },
10649     onHeaderChange : function()
10650     {
10651         var header = this.renderHeader();
10652         var table = this.el.select('table', true).first();
10653         
10654         this.headEl.remove();
10655         this.headEl = table.createChild(header, this.bodyEl, false);
10656         
10657         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10658             e.on('click', this.sort, this);
10659         }, this);
10660         
10661         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10662             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10663         }
10664         
10665     },
10666     
10667     onHiddenChange : function(colModel, colIndex, hidden)
10668     {
10669         /*
10670         this.cm.setHidden()
10671         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10672         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10673         
10674         this.CSS.updateRule(thSelector, "display", "");
10675         this.CSS.updateRule(tdSelector, "display", "");
10676         
10677         if(hidden){
10678             this.CSS.updateRule(thSelector, "display", "none");
10679             this.CSS.updateRule(tdSelector, "display", "none");
10680         }
10681         */
10682         // onload calls initCSS()
10683         this.onHeaderChange();
10684         this.onLoad();
10685     },
10686     
10687     setColumnWidth: function(col_index, width)
10688     {
10689         // width = "md-2 xs-2..."
10690         if(!this.colModel.config[col_index]) {
10691             return;
10692         }
10693         
10694         var w = width.split(" ");
10695         
10696         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10697         
10698         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10699         
10700         
10701         for(var j = 0; j < w.length; j++) {
10702             
10703             if(!w[j]) {
10704                 continue;
10705             }
10706             
10707             var size_cls = w[j].split("-");
10708             
10709             if(!Number.isInteger(size_cls[1] * 1)) {
10710                 continue;
10711             }
10712             
10713             if(!this.colModel.config[col_index][size_cls[0]]) {
10714                 continue;
10715             }
10716             
10717             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10718                 continue;
10719             }
10720             
10721             h_row[0].classList.replace(
10722                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10723                 "col-"+size_cls[0]+"-"+size_cls[1]
10724             );
10725             
10726             for(var i = 0; i < rows.length; i++) {
10727                 
10728                 var size_cls = w[j].split("-");
10729                 
10730                 if(!Number.isInteger(size_cls[1] * 1)) {
10731                     continue;
10732                 }
10733                 
10734                 if(!this.colModel.config[col_index][size_cls[0]]) {
10735                     continue;
10736                 }
10737                 
10738                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10739                     continue;
10740                 }
10741                 
10742                 rows[i].classList.replace(
10743                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10744                     "col-"+size_cls[0]+"-"+size_cls[1]
10745                 );
10746             }
10747             
10748             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10749         }
10750     }
10751 });
10752
10753 // currently only used to find the split on drag.. 
10754 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10755
10756 /**
10757  * @depricated
10758 */
10759 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10760 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10761 /*
10762  * - LGPL
10763  *
10764  * table cell
10765  * 
10766  */
10767
10768 /**
10769  * @class Roo.bootstrap.TableCell
10770  * @extends Roo.bootstrap.Component
10771  * @children Roo.bootstrap.Component
10772  * @parent Roo.bootstrap.TableRow
10773  * Bootstrap TableCell class
10774  * 
10775  * @cfg {String} html cell contain text
10776  * @cfg {String} cls cell class
10777  * @cfg {String} tag cell tag (td|th) default td
10778  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10779  * @cfg {String} align Aligns the content in a cell
10780  * @cfg {String} axis Categorizes cells
10781  * @cfg {String} bgcolor Specifies the background color of a cell
10782  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10783  * @cfg {Number} colspan Specifies the number of columns a cell should span
10784  * @cfg {String} headers Specifies one or more header cells a cell is related to
10785  * @cfg {Number} height Sets the height of a cell
10786  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10787  * @cfg {Number} rowspan Sets the number of rows a cell should span
10788  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10789  * @cfg {String} valign Vertical aligns the content in a cell
10790  * @cfg {Number} width Specifies the width of a cell
10791  * 
10792  * @constructor
10793  * Create a new TableCell
10794  * @param {Object} config The config object
10795  */
10796
10797 Roo.bootstrap.TableCell = function(config){
10798     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10799 };
10800
10801 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10802     
10803     html: false,
10804     cls: false,
10805     tag: false,
10806     abbr: false,
10807     align: false,
10808     axis: false,
10809     bgcolor: false,
10810     charoff: false,
10811     colspan: false,
10812     headers: false,
10813     height: false,
10814     nowrap: false,
10815     rowspan: false,
10816     scope: false,
10817     valign: false,
10818     width: false,
10819     
10820     
10821     getAutoCreate : function(){
10822         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10823         
10824         cfg = {
10825             tag: 'td'
10826         };
10827         
10828         if(this.tag){
10829             cfg.tag = this.tag;
10830         }
10831         
10832         if (this.html) {
10833             cfg.html=this.html
10834         }
10835         if (this.cls) {
10836             cfg.cls=this.cls
10837         }
10838         if (this.abbr) {
10839             cfg.abbr=this.abbr
10840         }
10841         if (this.align) {
10842             cfg.align=this.align
10843         }
10844         if (this.axis) {
10845             cfg.axis=this.axis
10846         }
10847         if (this.bgcolor) {
10848             cfg.bgcolor=this.bgcolor
10849         }
10850         if (this.charoff) {
10851             cfg.charoff=this.charoff
10852         }
10853         if (this.colspan) {
10854             cfg.colspan=this.colspan
10855         }
10856         if (this.headers) {
10857             cfg.headers=this.headers
10858         }
10859         if (this.height) {
10860             cfg.height=this.height
10861         }
10862         if (this.nowrap) {
10863             cfg.nowrap=this.nowrap
10864         }
10865         if (this.rowspan) {
10866             cfg.rowspan=this.rowspan
10867         }
10868         if (this.scope) {
10869             cfg.scope=this.scope
10870         }
10871         if (this.valign) {
10872             cfg.valign=this.valign
10873         }
10874         if (this.width) {
10875             cfg.width=this.width
10876         }
10877         
10878         
10879         return cfg;
10880     }
10881    
10882 });
10883
10884  
10885
10886  /*
10887  * - LGPL
10888  *
10889  * table row
10890  * 
10891  */
10892
10893 /**
10894  * @class Roo.bootstrap.TableRow
10895  * @extends Roo.bootstrap.Component
10896  * @children Roo.bootstrap.TableCell
10897  * @parent Roo.bootstrap.TableBody
10898  * Bootstrap TableRow class
10899  * @cfg {String} cls row class
10900  * @cfg {String} align Aligns the content in a table row
10901  * @cfg {String} bgcolor Specifies a background color for a table row
10902  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10903  * @cfg {String} valign Vertical aligns the content in a table row
10904  * 
10905  * @constructor
10906  * Create a new TableRow
10907  * @param {Object} config The config object
10908  */
10909
10910 Roo.bootstrap.TableRow = function(config){
10911     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10912 };
10913
10914 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10915     
10916     cls: false,
10917     align: false,
10918     bgcolor: false,
10919     charoff: false,
10920     valign: false,
10921     
10922     getAutoCreate : function(){
10923         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10924         
10925         cfg = {
10926             tag: 'tr'
10927         };
10928             
10929         if(this.cls){
10930             cfg.cls = this.cls;
10931         }
10932         if(this.align){
10933             cfg.align = this.align;
10934         }
10935         if(this.bgcolor){
10936             cfg.bgcolor = this.bgcolor;
10937         }
10938         if(this.charoff){
10939             cfg.charoff = this.charoff;
10940         }
10941         if(this.valign){
10942             cfg.valign = this.valign;
10943         }
10944         
10945         return cfg;
10946     }
10947    
10948 });
10949
10950  
10951
10952  /*
10953  * - LGPL
10954  *
10955  * table body
10956  * 
10957  */
10958
10959 /**
10960  * @class Roo.bootstrap.TableBody
10961  * @extends Roo.bootstrap.Component
10962  * @children Roo.bootstrap.TableRow
10963  * @parent Roo.bootstrap.Table
10964  * Bootstrap TableBody class
10965  * @cfg {String} cls element class
10966  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10967  * @cfg {String} align Aligns the content inside the element
10968  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10969  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10970  * 
10971  * @constructor
10972  * Create a new TableBody
10973  * @param {Object} config The config object
10974  */
10975
10976 Roo.bootstrap.TableBody = function(config){
10977     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10978 };
10979
10980 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10981     
10982     cls: false,
10983     tag: false,
10984     align: false,
10985     charoff: false,
10986     valign: false,
10987     
10988     getAutoCreate : function(){
10989         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10990         
10991         cfg = {
10992             tag: 'tbody'
10993         };
10994             
10995         if (this.cls) {
10996             cfg.cls=this.cls
10997         }
10998         if(this.tag){
10999             cfg.tag = this.tag;
11000         }
11001         
11002         if(this.align){
11003             cfg.align = this.align;
11004         }
11005         if(this.charoff){
11006             cfg.charoff = this.charoff;
11007         }
11008         if(this.valign){
11009             cfg.valign = this.valign;
11010         }
11011         
11012         return cfg;
11013     }
11014     
11015     
11016 //    initEvents : function()
11017 //    {
11018 //        
11019 //        if(!this.store){
11020 //            return;
11021 //        }
11022 //        
11023 //        this.store = Roo.factory(this.store, Roo.data);
11024 //        this.store.on('load', this.onLoad, this);
11025 //        
11026 //        this.store.load();
11027 //        
11028 //    },
11029 //    
11030 //    onLoad: function () 
11031 //    {   
11032 //        this.fireEvent('load', this);
11033 //    }
11034 //    
11035 //   
11036 });
11037
11038  
11039
11040  /*
11041  * Based on:
11042  * Ext JS Library 1.1.1
11043  * Copyright(c) 2006-2007, Ext JS, LLC.
11044  *
11045  * Originally Released Under LGPL - original licence link has changed is not relivant.
11046  *
11047  * Fork - LGPL
11048  * <script type="text/javascript">
11049  */
11050
11051 // as we use this in bootstrap.
11052 Roo.namespace('Roo.form');
11053  /**
11054  * @class Roo.form.Action
11055  * Internal Class used to handle form actions
11056  * @constructor
11057  * @param {Roo.form.BasicForm} el The form element or its id
11058  * @param {Object} config Configuration options
11059  */
11060
11061  
11062  
11063 // define the action interface
11064 Roo.form.Action = function(form, options){
11065     this.form = form;
11066     this.options = options || {};
11067 };
11068 /**
11069  * Client Validation Failed
11070  * @const 
11071  */
11072 Roo.form.Action.CLIENT_INVALID = 'client';
11073 /**
11074  * Server Validation Failed
11075  * @const 
11076  */
11077 Roo.form.Action.SERVER_INVALID = 'server';
11078  /**
11079  * Connect to Server Failed
11080  * @const 
11081  */
11082 Roo.form.Action.CONNECT_FAILURE = 'connect';
11083 /**
11084  * Reading Data from Server Failed
11085  * @const 
11086  */
11087 Roo.form.Action.LOAD_FAILURE = 'load';
11088
11089 Roo.form.Action.prototype = {
11090     type : 'default',
11091     failureType : undefined,
11092     response : undefined,
11093     result : undefined,
11094
11095     // interface method
11096     run : function(options){
11097
11098     },
11099
11100     // interface method
11101     success : function(response){
11102
11103     },
11104
11105     // interface method
11106     handleResponse : function(response){
11107
11108     },
11109
11110     // default connection failure
11111     failure : function(response){
11112         
11113         this.response = response;
11114         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11115         this.form.afterAction(this, false);
11116     },
11117
11118     processResponse : function(response){
11119         this.response = response;
11120         if(!response.responseText){
11121             return true;
11122         }
11123         this.result = this.handleResponse(response);
11124         return this.result;
11125     },
11126
11127     // utility functions used internally
11128     getUrl : function(appendParams){
11129         var url = this.options.url || this.form.url || this.form.el.dom.action;
11130         if(appendParams){
11131             var p = this.getParams();
11132             if(p){
11133                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11134             }
11135         }
11136         return url;
11137     },
11138
11139     getMethod : function(){
11140         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11141     },
11142
11143     getParams : function(){
11144         var bp = this.form.baseParams;
11145         var p = this.options.params;
11146         if(p){
11147             if(typeof p == "object"){
11148                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11149             }else if(typeof p == 'string' && bp){
11150                 p += '&' + Roo.urlEncode(bp);
11151             }
11152         }else if(bp){
11153             p = Roo.urlEncode(bp);
11154         }
11155         return p;
11156     },
11157
11158     createCallback : function(){
11159         return {
11160             success: this.success,
11161             failure: this.failure,
11162             scope: this,
11163             timeout: (this.form.timeout*1000),
11164             upload: this.form.fileUpload ? this.success : undefined
11165         };
11166     }
11167 };
11168
11169 Roo.form.Action.Submit = function(form, options){
11170     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11171 };
11172
11173 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11174     type : 'submit',
11175
11176     haveProgress : false,
11177     uploadComplete : false,
11178     
11179     // uploadProgress indicator.
11180     uploadProgress : function()
11181     {
11182         if (!this.form.progressUrl) {
11183             return;
11184         }
11185         
11186         if (!this.haveProgress) {
11187             Roo.MessageBox.progress("Uploading", "Uploading");
11188         }
11189         if (this.uploadComplete) {
11190            Roo.MessageBox.hide();
11191            return;
11192         }
11193         
11194         this.haveProgress = true;
11195    
11196         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11197         
11198         var c = new Roo.data.Connection();
11199         c.request({
11200             url : this.form.progressUrl,
11201             params: {
11202                 id : uid
11203             },
11204             method: 'GET',
11205             success : function(req){
11206                //console.log(data);
11207                 var rdata = false;
11208                 var edata;
11209                 try  {
11210                    rdata = Roo.decode(req.responseText)
11211                 } catch (e) {
11212                     Roo.log("Invalid data from server..");
11213                     Roo.log(edata);
11214                     return;
11215                 }
11216                 if (!rdata || !rdata.success) {
11217                     Roo.log(rdata);
11218                     Roo.MessageBox.alert(Roo.encode(rdata));
11219                     return;
11220                 }
11221                 var data = rdata.data;
11222                 
11223                 if (this.uploadComplete) {
11224                    Roo.MessageBox.hide();
11225                    return;
11226                 }
11227                    
11228                 if (data){
11229                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11230                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11231                     );
11232                 }
11233                 this.uploadProgress.defer(2000,this);
11234             },
11235        
11236             failure: function(data) {
11237                 Roo.log('progress url failed ');
11238                 Roo.log(data);
11239             },
11240             scope : this
11241         });
11242            
11243     },
11244     
11245     
11246     run : function()
11247     {
11248         // run get Values on the form, so it syncs any secondary forms.
11249         this.form.getValues();
11250         
11251         var o = this.options;
11252         var method = this.getMethod();
11253         var isPost = method == 'POST';
11254         if(o.clientValidation === false || this.form.isValid()){
11255             
11256             if (this.form.progressUrl) {
11257                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11258                     (new Date() * 1) + '' + Math.random());
11259                     
11260             } 
11261             
11262             
11263             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11264                 form:this.form.el.dom,
11265                 url:this.getUrl(!isPost),
11266                 method: method,
11267                 params:isPost ? this.getParams() : null,
11268                 isUpload: this.form.fileUpload,
11269                 formData : this.form.formData
11270             }));
11271             
11272             this.uploadProgress();
11273
11274         }else if (o.clientValidation !== false){ // client validation failed
11275             this.failureType = Roo.form.Action.CLIENT_INVALID;
11276             this.form.afterAction(this, false);
11277         }
11278     },
11279
11280     success : function(response)
11281     {
11282         this.uploadComplete= true;
11283         if (this.haveProgress) {
11284             Roo.MessageBox.hide();
11285         }
11286         
11287         
11288         var result = this.processResponse(response);
11289         if(result === true || result.success){
11290             this.form.afterAction(this, true);
11291             return;
11292         }
11293         if(result.errors){
11294             this.form.markInvalid(result.errors);
11295             this.failureType = Roo.form.Action.SERVER_INVALID;
11296         }
11297         this.form.afterAction(this, false);
11298     },
11299     failure : function(response)
11300     {
11301         this.uploadComplete= true;
11302         if (this.haveProgress) {
11303             Roo.MessageBox.hide();
11304         }
11305         
11306         this.response = response;
11307         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11308         this.form.afterAction(this, false);
11309     },
11310     
11311     handleResponse : function(response){
11312         if(this.form.errorReader){
11313             var rs = this.form.errorReader.read(response);
11314             var errors = [];
11315             if(rs.records){
11316                 for(var i = 0, len = rs.records.length; i < len; i++) {
11317                     var r = rs.records[i];
11318                     errors[i] = r.data;
11319                 }
11320             }
11321             if(errors.length < 1){
11322                 errors = null;
11323             }
11324             return {
11325                 success : rs.success,
11326                 errors : errors
11327             };
11328         }
11329         var ret = false;
11330         try {
11331             ret = Roo.decode(response.responseText);
11332         } catch (e) {
11333             ret = {
11334                 success: false,
11335                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11336                 errors : []
11337             };
11338         }
11339         return ret;
11340         
11341     }
11342 });
11343
11344
11345 Roo.form.Action.Load = function(form, options){
11346     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11347     this.reader = this.form.reader;
11348 };
11349
11350 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11351     type : 'load',
11352
11353     run : function(){
11354         
11355         Roo.Ajax.request(Roo.apply(
11356                 this.createCallback(), {
11357                     method:this.getMethod(),
11358                     url:this.getUrl(false),
11359                     params:this.getParams()
11360         }));
11361     },
11362
11363     success : function(response){
11364         
11365         var result = this.processResponse(response);
11366         if(result === true || !result.success || !result.data){
11367             this.failureType = Roo.form.Action.LOAD_FAILURE;
11368             this.form.afterAction(this, false);
11369             return;
11370         }
11371         this.form.clearInvalid();
11372         this.form.setValues(result.data);
11373         this.form.afterAction(this, true);
11374     },
11375
11376     handleResponse : function(response){
11377         if(this.form.reader){
11378             var rs = this.form.reader.read(response);
11379             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11380             return {
11381                 success : rs.success,
11382                 data : data
11383             };
11384         }
11385         return Roo.decode(response.responseText);
11386     }
11387 });
11388
11389 Roo.form.Action.ACTION_TYPES = {
11390     'load' : Roo.form.Action.Load,
11391     'submit' : Roo.form.Action.Submit
11392 };/*
11393  * - LGPL
11394  *
11395  * form
11396  *
11397  */
11398
11399 /**
11400  * @class Roo.bootstrap.form.Form
11401  * @extends Roo.bootstrap.Component
11402  * @children Roo.bootstrap.Component
11403  * Bootstrap Form class
11404  * @cfg {String} method  GET | POST (default POST)
11405  * @cfg {String} labelAlign top | left (default top)
11406  * @cfg {String} align left  | right - for navbars
11407  * @cfg {Boolean} loadMask load mask when submit (default true)
11408
11409  *
11410  * @constructor
11411  * Create a new Form
11412  * @param {Object} config The config object
11413  */
11414
11415
11416 Roo.bootstrap.form.Form = function(config){
11417     
11418     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11419     
11420     Roo.bootstrap.form.Form.popover.apply();
11421     
11422     this.addEvents({
11423         /**
11424          * @event clientvalidation
11425          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11426          * @param {Form} this
11427          * @param {Boolean} valid true if the form has passed client-side validation
11428          */
11429         clientvalidation: true,
11430         /**
11431          * @event beforeaction
11432          * Fires before any action is performed. Return false to cancel the action.
11433          * @param {Form} this
11434          * @param {Action} action The action to be performed
11435          */
11436         beforeaction: true,
11437         /**
11438          * @event actionfailed
11439          * Fires when an action fails.
11440          * @param {Form} this
11441          * @param {Action} action The action that failed
11442          */
11443         actionfailed : true,
11444         /**
11445          * @event actioncomplete
11446          * Fires when an action is completed.
11447          * @param {Form} this
11448          * @param {Action} action The action that completed
11449          */
11450         actioncomplete : true
11451     });
11452 };
11453
11454 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11455
11456      /**
11457      * @cfg {String} method
11458      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11459      */
11460     method : 'POST',
11461     /**
11462      * @cfg {String} url
11463      * The URL to use for form actions if one isn't supplied in the action options.
11464      */
11465     /**
11466      * @cfg {Boolean} fileUpload
11467      * Set to true if this form is a file upload.
11468      */
11469
11470     /**
11471      * @cfg {Object} baseParams
11472      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11473      */
11474
11475     /**
11476      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11477      */
11478     timeout: 30,
11479     /**
11480      * @cfg {Sting} align (left|right) for navbar forms
11481      */
11482     align : 'left',
11483
11484     // private
11485     activeAction : null,
11486
11487     /**
11488      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11489      * element by passing it or its id or mask the form itself by passing in true.
11490      * @type Mixed
11491      */
11492     waitMsgTarget : false,
11493
11494     loadMask : true,
11495     
11496     /**
11497      * @cfg {Boolean} errorMask (true|false) default false
11498      */
11499     errorMask : false,
11500     
11501     /**
11502      * @cfg {Number} maskOffset Default 100
11503      */
11504     maskOffset : 100,
11505     
11506     /**
11507      * @cfg {Boolean} maskBody
11508      */
11509     maskBody : false,
11510
11511     getAutoCreate : function(){
11512
11513         var cfg = {
11514             tag: 'form',
11515             method : this.method || 'POST',
11516             id : this.id || Roo.id(),
11517             cls : ''
11518         };
11519         if (this.parent().xtype.match(/^Nav/)) {
11520             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11521
11522         }
11523
11524         if (this.labelAlign == 'left' ) {
11525             cfg.cls += ' form-horizontal';
11526         }
11527
11528
11529         return cfg;
11530     },
11531     initEvents : function()
11532     {
11533         this.el.on('submit', this.onSubmit, this);
11534         // this was added as random key presses on the form where triggering form submit.
11535         this.el.on('keypress', function(e) {
11536             if (e.getCharCode() != 13) {
11537                 return true;
11538             }
11539             // we might need to allow it for textareas.. and some other items.
11540             // check e.getTarget().
11541
11542             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11543                 return true;
11544             }
11545
11546             Roo.log("keypress blocked");
11547
11548             e.preventDefault();
11549             return false;
11550         });
11551         
11552     },
11553     // private
11554     onSubmit : function(e){
11555         e.stopEvent();
11556     },
11557
11558      /**
11559      * Returns true if client-side validation on the form is successful.
11560      * @return Boolean
11561      */
11562     isValid : function(){
11563         var items = this.getItems();
11564         var valid = true;
11565         var target = false;
11566         
11567         items.each(function(f){
11568             
11569             if(f.validate()){
11570                 return;
11571             }
11572             
11573             Roo.log('invalid field: ' + f.name);
11574             
11575             valid = false;
11576
11577             if(!target && f.el.isVisible(true)){
11578                 target = f;
11579             }
11580            
11581         });
11582         
11583         if(this.errorMask && !valid){
11584             Roo.bootstrap.form.Form.popover.mask(this, target);
11585         }
11586         
11587         return valid;
11588     },
11589     
11590     /**
11591      * Returns true if any fields in this form have changed since their original load.
11592      * @return Boolean
11593      */
11594     isDirty : function(){
11595         var dirty = false;
11596         var items = this.getItems();
11597         items.each(function(f){
11598            if(f.isDirty()){
11599                dirty = true;
11600                return false;
11601            }
11602            return true;
11603         });
11604         return dirty;
11605     },
11606      /**
11607      * Performs a predefined action (submit or load) or custom actions you define on this form.
11608      * @param {String} actionName The name of the action type
11609      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11610      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11611      * accept other config options):
11612      * <pre>
11613 Property          Type             Description
11614 ----------------  ---------------  ----------------------------------------------------------------------------------
11615 url               String           The url for the action (defaults to the form's url)
11616 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11617 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11618 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11619                                    validate the form on the client (defaults to false)
11620      * </pre>
11621      * @return {BasicForm} this
11622      */
11623     doAction : function(action, options){
11624         if(typeof action == 'string'){
11625             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11626         }
11627         if(this.fireEvent('beforeaction', this, action) !== false){
11628             this.beforeAction(action);
11629             action.run.defer(100, action);
11630         }
11631         return this;
11632     },
11633
11634     // private
11635     beforeAction : function(action){
11636         var o = action.options;
11637         
11638         if(this.loadMask){
11639             
11640             if(this.maskBody){
11641                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11642             } else {
11643                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11644             }
11645         }
11646         // not really supported yet.. ??
11647
11648         //if(this.waitMsgTarget === true){
11649         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11650         //}else if(this.waitMsgTarget){
11651         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11652         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11653         //}else {
11654         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11655        // }
11656
11657     },
11658
11659     // private
11660     afterAction : function(action, success){
11661         this.activeAction = null;
11662         var o = action.options;
11663
11664         if(this.loadMask){
11665             
11666             if(this.maskBody){
11667                 Roo.get(document.body).unmask();
11668             } else {
11669                 this.el.unmask();
11670             }
11671         }
11672         
11673         //if(this.waitMsgTarget === true){
11674 //            this.el.unmask();
11675         //}else if(this.waitMsgTarget){
11676         //    this.waitMsgTarget.unmask();
11677         //}else{
11678         //    Roo.MessageBox.updateProgress(1);
11679         //    Roo.MessageBox.hide();
11680        // }
11681         //
11682         if(success){
11683             if(o.reset){
11684                 this.reset();
11685             }
11686             Roo.callback(o.success, o.scope, [this, action]);
11687             this.fireEvent('actioncomplete', this, action);
11688
11689         }else{
11690
11691             // failure condition..
11692             // we have a scenario where updates need confirming.
11693             // eg. if a locking scenario exists..
11694             // we look for { errors : { needs_confirm : true }} in the response.
11695             if (
11696                 (typeof(action.result) != 'undefined')  &&
11697                 (typeof(action.result.errors) != 'undefined')  &&
11698                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11699            ){
11700                 var _t = this;
11701                 Roo.log("not supported yet");
11702                  /*
11703
11704                 Roo.MessageBox.confirm(
11705                     "Change requires confirmation",
11706                     action.result.errorMsg,
11707                     function(r) {
11708                         if (r != 'yes') {
11709                             return;
11710                         }
11711                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11712                     }
11713
11714                 );
11715                 */
11716
11717
11718                 return;
11719             }
11720
11721             Roo.callback(o.failure, o.scope, [this, action]);
11722             // show an error message if no failed handler is set..
11723             if (!this.hasListener('actionfailed')) {
11724                 Roo.log("need to add dialog support");
11725                 /*
11726                 Roo.MessageBox.alert("Error",
11727                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11728                         action.result.errorMsg :
11729                         "Saving Failed, please check your entries or try again"
11730                 );
11731                 */
11732             }
11733
11734             this.fireEvent('actionfailed', this, action);
11735         }
11736
11737     },
11738     /**
11739      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11740      * @param {String} id The value to search for
11741      * @return Field
11742      */
11743     findField : function(id){
11744         var items = this.getItems();
11745         var field = items.get(id);
11746         if(!field){
11747              items.each(function(f){
11748                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11749                     field = f;
11750                     return false;
11751                 }
11752                 return true;
11753             });
11754         }
11755         return field || null;
11756     },
11757      /**
11758      * Mark fields in this form invalid in bulk.
11759      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11760      * @return {BasicForm} this
11761      */
11762     markInvalid : function(errors){
11763         if(errors instanceof Array){
11764             for(var i = 0, len = errors.length; i < len; i++){
11765                 var fieldError = errors[i];
11766                 var f = this.findField(fieldError.id);
11767                 if(f){
11768                     f.markInvalid(fieldError.msg);
11769                 }
11770             }
11771         }else{
11772             var field, id;
11773             for(id in errors){
11774                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11775                     field.markInvalid(errors[id]);
11776                 }
11777             }
11778         }
11779         //Roo.each(this.childForms || [], function (f) {
11780         //    f.markInvalid(errors);
11781         //});
11782
11783         return this;
11784     },
11785
11786     /**
11787      * Set values for fields in this form in bulk.
11788      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11789      * @return {BasicForm} this
11790      */
11791     setValues : function(values){
11792         if(values instanceof Array){ // array of objects
11793             for(var i = 0, len = values.length; i < len; i++){
11794                 var v = values[i];
11795                 var f = this.findField(v.id);
11796                 if(f){
11797                     f.setValue(v.value);
11798                     if(this.trackResetOnLoad){
11799                         f.originalValue = f.getValue();
11800                     }
11801                 }
11802             }
11803         }else{ // object hash
11804             var field, id;
11805             for(id in values){
11806                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11807
11808                     if (field.setFromData &&
11809                         field.valueField &&
11810                         field.displayField &&
11811                         // combos' with local stores can
11812                         // be queried via setValue()
11813                         // to set their value..
11814                         (field.store && !field.store.isLocal)
11815                         ) {
11816                         // it's a combo
11817                         var sd = { };
11818                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11819                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11820                         field.setFromData(sd);
11821
11822                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11823                         
11824                         field.setFromData(values);
11825                         
11826                     } else {
11827                         field.setValue(values[id]);
11828                     }
11829
11830
11831                     if(this.trackResetOnLoad){
11832                         field.originalValue = field.getValue();
11833                     }
11834                 }
11835             }
11836         }
11837
11838         //Roo.each(this.childForms || [], function (f) {
11839         //    f.setValues(values);
11840         //});
11841
11842         return this;
11843     },
11844
11845     /**
11846      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11847      * they are returned as an array.
11848      * @param {Boolean} asString
11849      * @return {Object}
11850      */
11851     getValues : function(asString){
11852         //if (this.childForms) {
11853             // copy values from the child forms
11854         //    Roo.each(this.childForms, function (f) {
11855         //        this.setValues(f.getValues());
11856         //    }, this);
11857         //}
11858
11859
11860
11861         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11862         if(asString === true){
11863             return fs;
11864         }
11865         return Roo.urlDecode(fs);
11866     },
11867
11868     /**
11869      * Returns the fields in this form as an object with key/value pairs.
11870      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11871      * @return {Object}
11872      */
11873     getFieldValues : function(with_hidden)
11874     {
11875         var items = this.getItems();
11876         var ret = {};
11877         items.each(function(f){
11878             
11879             if (!f.getName()) {
11880                 return;
11881             }
11882             
11883             var v = f.getValue();
11884             
11885             if (f.inputType =='radio') {
11886                 if (typeof(ret[f.getName()]) == 'undefined') {
11887                     ret[f.getName()] = ''; // empty..
11888                 }
11889
11890                 if (!f.el.dom.checked) {
11891                     return;
11892
11893                 }
11894                 v = f.el.dom.value;
11895
11896             }
11897             
11898             if(f.xtype == 'MoneyField'){
11899                 ret[f.currencyName] = f.getCurrency();
11900             }
11901
11902             // not sure if this supported any more..
11903             if ((typeof(v) == 'object') && f.getRawValue) {
11904                 v = f.getRawValue() ; // dates..
11905             }
11906             // combo boxes where name != hiddenName...
11907             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11908                 ret[f.name] = f.getRawValue();
11909             }
11910             ret[f.getName()] = v;
11911         });
11912
11913         return ret;
11914     },
11915
11916     /**
11917      * Clears all invalid messages in this form.
11918      * @return {BasicForm} this
11919      */
11920     clearInvalid : function(){
11921         var items = this.getItems();
11922
11923         items.each(function(f){
11924            f.clearInvalid();
11925         });
11926
11927         return this;
11928     },
11929
11930     /**
11931      * Resets this form.
11932      * @return {BasicForm} this
11933      */
11934     reset : function(){
11935         var items = this.getItems();
11936         items.each(function(f){
11937             f.reset();
11938         });
11939
11940         Roo.each(this.childForms || [], function (f) {
11941             f.reset();
11942         });
11943
11944
11945         return this;
11946     },
11947     
11948     getItems : function()
11949     {
11950         var r=new Roo.util.MixedCollection(false, function(o){
11951             return o.id || (o.id = Roo.id());
11952         });
11953         var iter = function(el) {
11954             if (el.inputEl) {
11955                 r.add(el);
11956             }
11957             if (!el.items) {
11958                 return;
11959             }
11960             Roo.each(el.items,function(e) {
11961                 iter(e);
11962             });
11963         };
11964
11965         iter(this);
11966         return r;
11967     },
11968     
11969     hideFields : function(items)
11970     {
11971         Roo.each(items, function(i){
11972             
11973             var f = this.findField(i);
11974             
11975             if(!f){
11976                 return;
11977             }
11978             
11979             f.hide();
11980             
11981         }, this);
11982     },
11983     
11984     showFields : function(items)
11985     {
11986         Roo.each(items, function(i){
11987             
11988             var f = this.findField(i);
11989             
11990             if(!f){
11991                 return;
11992             }
11993             
11994             f.show();
11995             
11996         }, this);
11997     }
11998
11999 });
12000
12001 Roo.apply(Roo.bootstrap.form.Form, {
12002     
12003     popover : {
12004         
12005         padding : 5,
12006         
12007         isApplied : false,
12008         
12009         isMasked : false,
12010         
12011         form : false,
12012         
12013         target : false,
12014         
12015         toolTip : false,
12016         
12017         intervalID : false,
12018         
12019         maskEl : false,
12020         
12021         apply : function()
12022         {
12023             if(this.isApplied){
12024                 return;
12025             }
12026             
12027             this.maskEl = {
12028                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12029                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12030                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12031                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12032             };
12033             
12034             this.maskEl.top.enableDisplayMode("block");
12035             this.maskEl.left.enableDisplayMode("block");
12036             this.maskEl.bottom.enableDisplayMode("block");
12037             this.maskEl.right.enableDisplayMode("block");
12038             
12039             this.toolTip = new Roo.bootstrap.Tooltip({
12040                 cls : 'roo-form-error-popover',
12041                 alignment : {
12042                     'left' : ['r-l', [-2,0], 'right'],
12043                     'right' : ['l-r', [2,0], 'left'],
12044                     'bottom' : ['tl-bl', [0,2], 'top'],
12045                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12046                 }
12047             });
12048             
12049             this.toolTip.render(Roo.get(document.body));
12050
12051             this.toolTip.el.enableDisplayMode("block");
12052             
12053             Roo.get(document.body).on('click', function(){
12054                 this.unmask();
12055             }, this);
12056             
12057             Roo.get(document.body).on('touchstart', function(){
12058                 this.unmask();
12059             }, this);
12060             
12061             this.isApplied = true
12062         },
12063         
12064         mask : function(form, target)
12065         {
12066             this.form = form;
12067             
12068             this.target = target;
12069             
12070             if(!this.form.errorMask || !target.el){
12071                 return;
12072             }
12073             
12074             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12075             
12076             Roo.log(scrollable);
12077             
12078             var ot = this.target.el.calcOffsetsTo(scrollable);
12079             
12080             var scrollTo = ot[1] - this.form.maskOffset;
12081             
12082             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12083             
12084             scrollable.scrollTo('top', scrollTo);
12085             
12086             var box = this.target.el.getBox();
12087             Roo.log(box);
12088             var zIndex = Roo.bootstrap.Modal.zIndex++;
12089
12090             
12091             this.maskEl.top.setStyle('position', 'absolute');
12092             this.maskEl.top.setStyle('z-index', zIndex);
12093             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12094             this.maskEl.top.setLeft(0);
12095             this.maskEl.top.setTop(0);
12096             this.maskEl.top.show();
12097             
12098             this.maskEl.left.setStyle('position', 'absolute');
12099             this.maskEl.left.setStyle('z-index', zIndex);
12100             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12101             this.maskEl.left.setLeft(0);
12102             this.maskEl.left.setTop(box.y - this.padding);
12103             this.maskEl.left.show();
12104
12105             this.maskEl.bottom.setStyle('position', 'absolute');
12106             this.maskEl.bottom.setStyle('z-index', zIndex);
12107             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12108             this.maskEl.bottom.setLeft(0);
12109             this.maskEl.bottom.setTop(box.bottom + this.padding);
12110             this.maskEl.bottom.show();
12111
12112             this.maskEl.right.setStyle('position', 'absolute');
12113             this.maskEl.right.setStyle('z-index', zIndex);
12114             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12115             this.maskEl.right.setLeft(box.right + this.padding);
12116             this.maskEl.right.setTop(box.y - this.padding);
12117             this.maskEl.right.show();
12118
12119             this.toolTip.bindEl = this.target.el;
12120
12121             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12122
12123             var tip = this.target.blankText;
12124
12125             if(this.target.getValue() !== '' ) {
12126                 
12127                 if (this.target.invalidText.length) {
12128                     tip = this.target.invalidText;
12129                 } else if (this.target.regexText.length){
12130                     tip = this.target.regexText;
12131                 }
12132             }
12133
12134             this.toolTip.show(tip);
12135
12136             this.intervalID = window.setInterval(function() {
12137                 Roo.bootstrap.form.Form.popover.unmask();
12138             }, 10000);
12139
12140             window.onwheel = function(){ return false;};
12141             
12142             (function(){ this.isMasked = true; }).defer(500, this);
12143             
12144         },
12145         
12146         unmask : function()
12147         {
12148             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12149                 return;
12150             }
12151             
12152             this.maskEl.top.setStyle('position', 'absolute');
12153             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12154             this.maskEl.top.hide();
12155
12156             this.maskEl.left.setStyle('position', 'absolute');
12157             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12158             this.maskEl.left.hide();
12159
12160             this.maskEl.bottom.setStyle('position', 'absolute');
12161             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12162             this.maskEl.bottom.hide();
12163
12164             this.maskEl.right.setStyle('position', 'absolute');
12165             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12166             this.maskEl.right.hide();
12167             
12168             this.toolTip.hide();
12169             
12170             this.toolTip.el.hide();
12171             
12172             window.onwheel = function(){ return true;};
12173             
12174             if(this.intervalID){
12175                 window.clearInterval(this.intervalID);
12176                 this.intervalID = false;
12177             }
12178             
12179             this.isMasked = false;
12180             
12181         }
12182         
12183     }
12184     
12185 });
12186
12187 /*
12188  * Based on:
12189  * Ext JS Library 1.1.1
12190  * Copyright(c) 2006-2007, Ext JS, LLC.
12191  *
12192  * Originally Released Under LGPL - original licence link has changed is not relivant.
12193  *
12194  * Fork - LGPL
12195  * <script type="text/javascript">
12196  */
12197 /**
12198  * @class Roo.form.VTypes
12199  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12200  * @static
12201  */
12202 Roo.form.VTypes = function(){
12203     // closure these in so they are only created once.
12204     var alpha = /^[a-zA-Z_]+$/;
12205     var alphanum = /^[a-zA-Z0-9_]+$/;
12206     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12207     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12208
12209     // All these messages and functions are configurable
12210     return {
12211         /**
12212          * The function used to validate email addresses
12213          * @param {String} value The email address
12214          */
12215         email : function(v){
12216             return email.test(v);
12217         },
12218         /**
12219          * The error text to display when the email validation function returns false
12220          * @type String
12221          */
12222         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12223         /**
12224          * The keystroke filter mask to be applied on email input
12225          * @type RegExp
12226          */
12227         emailMask : /[a-z0-9_\.\-@]/i,
12228
12229         /**
12230          * The function used to validate URLs
12231          * @param {String} value The URL
12232          */
12233         url : function(v){
12234             return url.test(v);
12235         },
12236         /**
12237          * The error text to display when the url validation function returns false
12238          * @type String
12239          */
12240         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12241         
12242         /**
12243          * The function used to validate alpha values
12244          * @param {String} value The value
12245          */
12246         alpha : function(v){
12247             return alpha.test(v);
12248         },
12249         /**
12250          * The error text to display when the alpha validation function returns false
12251          * @type String
12252          */
12253         alphaText : 'This field should only contain letters and _',
12254         /**
12255          * The keystroke filter mask to be applied on alpha input
12256          * @type RegExp
12257          */
12258         alphaMask : /[a-z_]/i,
12259
12260         /**
12261          * The function used to validate alphanumeric values
12262          * @param {String} value The value
12263          */
12264         alphanum : function(v){
12265             return alphanum.test(v);
12266         },
12267         /**
12268          * The error text to display when the alphanumeric validation function returns false
12269          * @type String
12270          */
12271         alphanumText : 'This field should only contain letters, numbers and _',
12272         /**
12273          * The keystroke filter mask to be applied on alphanumeric input
12274          * @type RegExp
12275          */
12276         alphanumMask : /[a-z0-9_]/i
12277     };
12278 }();/*
12279  * - LGPL
12280  *
12281  * Input
12282  * 
12283  */
12284
12285 /**
12286  * @class Roo.bootstrap.form.Input
12287  * @extends Roo.bootstrap.Component
12288  * Bootstrap Input class
12289  * @cfg {Boolean} disabled is it disabled
12290  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12291  * @cfg {String} name name of the input
12292  * @cfg {string} fieldLabel - the label associated
12293  * @cfg {string} placeholder - placeholder to put in text.
12294  * @cfg {string} before - input group add on before
12295  * @cfg {string} after - input group add on after
12296  * @cfg {string} size - (lg|sm) or leave empty..
12297  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12298  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12299  * @cfg {Number} md colspan out of 12 for computer-sized screens
12300  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12301  * @cfg {string} value default value of the input
12302  * @cfg {Number} labelWidth set the width of label 
12303  * @cfg {Number} labellg set the width of label (1-12)
12304  * @cfg {Number} labelmd set the width of label (1-12)
12305  * @cfg {Number} labelsm set the width of label (1-12)
12306  * @cfg {Number} labelxs set the width of label (1-12)
12307  * @cfg {String} labelAlign (top|left)
12308  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12309  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12310  * @cfg {String} indicatorpos (left|right) default left
12311  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12312  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12313  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12314  * @cfg {Roo.bootstrap.Button} before Button to show before
12315  * @cfg {Roo.bootstrap.Button} afterButton to show before
12316  * @cfg {String} align (left|center|right) Default left
12317  * @cfg {Boolean} forceFeedback (true|false) Default false
12318  * 
12319  * @constructor
12320  * Create a new Input
12321  * @param {Object} config The config object
12322  */
12323
12324 Roo.bootstrap.form.Input = function(config){
12325     
12326     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12327     
12328     this.addEvents({
12329         /**
12330          * @event focus
12331          * Fires when this field receives input focus.
12332          * @param {Roo.form.Field} this
12333          */
12334         focus : true,
12335         /**
12336          * @event blur
12337          * Fires when this field loses input focus.
12338          * @param {Roo.form.Field} this
12339          */
12340         blur : true,
12341         /**
12342          * @event specialkey
12343          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12344          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12345          * @param {Roo.form.Field} this
12346          * @param {Roo.EventObject} e The event object
12347          */
12348         specialkey : true,
12349         /**
12350          * @event change
12351          * Fires just before the field blurs if the field value has changed.
12352          * @param {Roo.form.Field} this
12353          * @param {Mixed} newValue The new value
12354          * @param {Mixed} oldValue The original value
12355          */
12356         change : true,
12357         /**
12358          * @event invalid
12359          * Fires after the field has been marked as invalid.
12360          * @param {Roo.form.Field} this
12361          * @param {String} msg The validation message
12362          */
12363         invalid : true,
12364         /**
12365          * @event valid
12366          * Fires after the field has been validated with no errors.
12367          * @param {Roo.form.Field} this
12368          */
12369         valid : true,
12370          /**
12371          * @event keyup
12372          * Fires after the key up
12373          * @param {Roo.form.Field} this
12374          * @param {Roo.EventObject}  e The event Object
12375          */
12376         keyup : true,
12377         /**
12378          * @event paste
12379          * Fires after the user pastes into input
12380          * @param {Roo.form.Field} this
12381          * @param {Roo.EventObject}  e The event Object
12382          */
12383         paste : true
12384     });
12385 };
12386
12387 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12388      /**
12389      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12390       automatic validation (defaults to "keyup").
12391      */
12392     validationEvent : "keyup",
12393      /**
12394      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12395      */
12396     validateOnBlur : true,
12397     /**
12398      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12399      */
12400     validationDelay : 250,
12401      /**
12402      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12403      */
12404     focusClass : "x-form-focus",  // not needed???
12405     
12406        
12407     /**
12408      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12409      */
12410     invalidClass : "has-warning",
12411     
12412     /**
12413      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12414      */
12415     validClass : "has-success",
12416     
12417     /**
12418      * @cfg {Boolean} hasFeedback (true|false) default true
12419      */
12420     hasFeedback : true,
12421     
12422     /**
12423      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12424      */
12425     invalidFeedbackClass : "glyphicon-warning-sign",
12426     
12427     /**
12428      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12429      */
12430     validFeedbackClass : "glyphicon-ok",
12431     
12432     /**
12433      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12434      */
12435     selectOnFocus : false,
12436     
12437      /**
12438      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12439      */
12440     maskRe : null,
12441        /**
12442      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12443      */
12444     vtype : null,
12445     
12446       /**
12447      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12448      */
12449     disableKeyFilter : false,
12450     
12451        /**
12452      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12453      */
12454     disabled : false,
12455      /**
12456      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12457      */
12458     allowBlank : true,
12459     /**
12460      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12461      */
12462     blankText : "Please complete this mandatory field",
12463     
12464      /**
12465      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12466      */
12467     minLength : 0,
12468     /**
12469      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12470      */
12471     maxLength : Number.MAX_VALUE,
12472     /**
12473      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12474      */
12475     minLengthText : "The minimum length for this field is {0}",
12476     /**
12477      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12478      */
12479     maxLengthText : "The maximum length for this field is {0}",
12480   
12481     
12482     /**
12483      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12484      * If available, this function will be called only after the basic validators all return true, and will be passed the
12485      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12486      */
12487     validator : null,
12488     /**
12489      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12490      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12491      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12492      */
12493     regex : null,
12494     /**
12495      * @cfg {String} regexText -- Depricated - use Invalid Text
12496      */
12497     regexText : "",
12498     
12499     /**
12500      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12501      */
12502     invalidText : "",
12503     
12504     
12505     
12506     autocomplete: false,
12507     
12508     
12509     fieldLabel : '',
12510     inputType : 'text',
12511     
12512     name : false,
12513     placeholder: false,
12514     before : false,
12515     after : false,
12516     size : false,
12517     hasFocus : false,
12518     preventMark: false,
12519     isFormField : true,
12520     value : '',
12521     labelWidth : 2,
12522     labelAlign : false,
12523     readOnly : false,
12524     align : false,
12525     formatedValue : false,
12526     forceFeedback : false,
12527     
12528     indicatorpos : 'left',
12529     
12530     labellg : 0,
12531     labelmd : 0,
12532     labelsm : 0,
12533     labelxs : 0,
12534     
12535     capture : '',
12536     accept : '',
12537     
12538     parentLabelAlign : function()
12539     {
12540         var parent = this;
12541         while (parent.parent()) {
12542             parent = parent.parent();
12543             if (typeof(parent.labelAlign) !='undefined') {
12544                 return parent.labelAlign;
12545             }
12546         }
12547         return 'left';
12548         
12549     },
12550     
12551     getAutoCreate : function()
12552     {
12553         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12554         
12555         var id = Roo.id();
12556         
12557         var cfg = {};
12558         
12559         if(this.inputType != 'hidden'){
12560             cfg.cls = 'form-group' //input-group
12561         }
12562         
12563         var input =  {
12564             tag: 'input',
12565             id : id,
12566             type : this.inputType,
12567             value : this.value,
12568             cls : 'form-control',
12569             placeholder : this.placeholder || '',
12570             autocomplete : this.autocomplete || 'new-password'
12571         };
12572         if (this.inputType == 'file') {
12573             input.style = 'overflow:hidden'; // why not in CSS?
12574         }
12575         
12576         if(this.capture.length){
12577             input.capture = this.capture;
12578         }
12579         
12580         if(this.accept.length){
12581             input.accept = this.accept + "/*";
12582         }
12583         
12584         if(this.align){
12585             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12586         }
12587         
12588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12589             input.maxLength = this.maxLength;
12590         }
12591         
12592         if (this.disabled) {
12593             input.disabled=true;
12594         }
12595         
12596         if (this.readOnly) {
12597             input.readonly=true;
12598         }
12599         
12600         if (this.name) {
12601             input.name = this.name;
12602         }
12603         
12604         if (this.size) {
12605             input.cls += ' input-' + this.size;
12606         }
12607         
12608         var settings=this;
12609         ['xs','sm','md','lg'].map(function(size){
12610             if (settings[size]) {
12611                 cfg.cls += ' col-' + size + '-' + settings[size];
12612             }
12613         });
12614         
12615         var inputblock = input;
12616         
12617         var feedback = {
12618             tag: 'span',
12619             cls: 'glyphicon form-control-feedback'
12620         };
12621             
12622         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12623             
12624             inputblock = {
12625                 cls : 'has-feedback',
12626                 cn :  [
12627                     input,
12628                     feedback
12629                 ] 
12630             };  
12631         }
12632         
12633         if (this.before || this.after) {
12634             
12635             inputblock = {
12636                 cls : 'input-group',
12637                 cn :  [] 
12638             };
12639             
12640             if (this.before && typeof(this.before) == 'string') {
12641                 
12642                 inputblock.cn.push({
12643                     tag :'span',
12644                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12645                     html : this.before
12646                 });
12647             }
12648             if (this.before && typeof(this.before) == 'object') {
12649                 this.before = Roo.factory(this.before);
12650                 
12651                 inputblock.cn.push({
12652                     tag :'span',
12653                     cls : 'roo-input-before input-group-prepend   input-group-' +
12654                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12655                 });
12656             }
12657             
12658             inputblock.cn.push(input);
12659             
12660             if (this.after && typeof(this.after) == 'string') {
12661                 inputblock.cn.push({
12662                     tag :'span',
12663                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12664                     html : this.after
12665                 });
12666             }
12667             if (this.after && typeof(this.after) == 'object') {
12668                 this.after = Roo.factory(this.after);
12669                 
12670                 inputblock.cn.push({
12671                     tag :'span',
12672                     cls : 'roo-input-after input-group-append  input-group-' +
12673                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12674                 });
12675             }
12676             
12677             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12678                 inputblock.cls += ' has-feedback';
12679                 inputblock.cn.push(feedback);
12680             }
12681         };
12682         var indicator = {
12683             tag : 'i',
12684             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12685             tooltip : 'This field is required'
12686         };
12687         if (this.allowBlank ) {
12688             indicator.style = this.allowBlank ? ' display:none' : '';
12689         }
12690         if (align ==='left' && this.fieldLabel.length) {
12691             
12692             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12693             
12694             cfg.cn = [
12695                 indicator,
12696                 {
12697                     tag: 'label',
12698                     'for' :  id,
12699                     cls : 'control-label col-form-label',
12700                     html : this.fieldLabel
12701
12702                 },
12703                 {
12704                     cls : "", 
12705                     cn: [
12706                         inputblock
12707                     ]
12708                 }
12709             ];
12710             
12711             var labelCfg = cfg.cn[1];
12712             var contentCfg = cfg.cn[2];
12713             
12714             if(this.indicatorpos == 'right'){
12715                 cfg.cn = [
12716                     {
12717                         tag: 'label',
12718                         'for' :  id,
12719                         cls : 'control-label col-form-label',
12720                         cn : [
12721                             {
12722                                 tag : 'span',
12723                                 html : this.fieldLabel
12724                             },
12725                             indicator
12726                         ]
12727                     },
12728                     {
12729                         cls : "",
12730                         cn: [
12731                             inputblock
12732                         ]
12733                     }
12734
12735                 ];
12736                 
12737                 labelCfg = cfg.cn[0];
12738                 contentCfg = cfg.cn[1];
12739             
12740             }
12741             
12742             if(this.labelWidth > 12){
12743                 labelCfg.style = "width: " + this.labelWidth + 'px';
12744             }
12745             
12746             if(this.labelWidth < 13 && this.labelmd == 0){
12747                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12748             }
12749             
12750             if(this.labellg > 0){
12751                 labelCfg.cls += ' col-lg-' + this.labellg;
12752                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12753             }
12754             
12755             if(this.labelmd > 0){
12756                 labelCfg.cls += ' col-md-' + this.labelmd;
12757                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12758             }
12759             
12760             if(this.labelsm > 0){
12761                 labelCfg.cls += ' col-sm-' + this.labelsm;
12762                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12763             }
12764             
12765             if(this.labelxs > 0){
12766                 labelCfg.cls += ' col-xs-' + this.labelxs;
12767                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12768             }
12769             
12770             
12771         } else if ( this.fieldLabel.length) {
12772                 
12773             
12774             
12775             cfg.cn = [
12776                 {
12777                     tag : 'i',
12778                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12779                     tooltip : 'This field is required',
12780                     style : this.allowBlank ? ' display:none' : '' 
12781                 },
12782                 {
12783                     tag: 'label',
12784                    //cls : 'input-group-addon',
12785                     html : this.fieldLabel
12786
12787                 },
12788
12789                inputblock
12790
12791            ];
12792            
12793            if(this.indicatorpos == 'right'){
12794        
12795                 cfg.cn = [
12796                     {
12797                         tag: 'label',
12798                        //cls : 'input-group-addon',
12799                         html : this.fieldLabel
12800
12801                     },
12802                     {
12803                         tag : 'i',
12804                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12805                         tooltip : 'This field is required',
12806                         style : this.allowBlank ? ' display:none' : '' 
12807                     },
12808
12809                    inputblock
12810
12811                ];
12812
12813             }
12814
12815         } else {
12816             
12817             cfg.cn = [
12818
12819                     inputblock
12820
12821             ];
12822                 
12823                 
12824         };
12825         
12826         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12827            cfg.cls += ' navbar-form';
12828         }
12829         
12830         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12831             // on BS4 we do this only if not form 
12832             cfg.cls += ' navbar-form';
12833             cfg.tag = 'li';
12834         }
12835         
12836         return cfg;
12837         
12838     },
12839     /**
12840      * return the real input element.
12841      */
12842     inputEl: function ()
12843     {
12844         return this.el.select('input.form-control',true).first();
12845     },
12846     
12847     tooltipEl : function()
12848     {
12849         return this.inputEl();
12850     },
12851     
12852     indicatorEl : function()
12853     {
12854         if (Roo.bootstrap.version == 4) {
12855             return false; // not enabled in v4 yet.
12856         }
12857         
12858         var indicator = this.el.select('i.roo-required-indicator',true).first();
12859         
12860         if(!indicator){
12861             return false;
12862         }
12863         
12864         return indicator;
12865         
12866     },
12867     
12868     setDisabled : function(v)
12869     {
12870         var i  = this.inputEl().dom;
12871         if (!v) {
12872             i.removeAttribute('disabled');
12873             return;
12874             
12875         }
12876         i.setAttribute('disabled','true');
12877     },
12878     initEvents : function()
12879     {
12880           
12881         this.inputEl().on("keydown" , this.fireKey,  this);
12882         this.inputEl().on("focus", this.onFocus,  this);
12883         this.inputEl().on("blur", this.onBlur,  this);
12884         
12885         this.inputEl().relayEvent('keyup', this);
12886         this.inputEl().relayEvent('paste', this);
12887         
12888         this.indicator = this.indicatorEl();
12889         
12890         if(this.indicator){
12891             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12892         }
12893  
12894         // reference to original value for reset
12895         this.originalValue = this.getValue();
12896         //Roo.form.TextField.superclass.initEvents.call(this);
12897         if(this.validationEvent == 'keyup'){
12898             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12899             this.inputEl().on('keyup', this.filterValidation, this);
12900         }
12901         else if(this.validationEvent !== false){
12902             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12903         }
12904         
12905         if(this.selectOnFocus){
12906             this.on("focus", this.preFocus, this);
12907             
12908         }
12909         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12910             this.inputEl().on("keypress", this.filterKeys, this);
12911         } else {
12912             this.inputEl().relayEvent('keypress', this);
12913         }
12914        /* if(this.grow){
12915             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12916             this.el.on("click", this.autoSize,  this);
12917         }
12918         */
12919         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12920             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12921         }
12922         
12923         if (typeof(this.before) == 'object') {
12924             this.before.render(this.el.select('.roo-input-before',true).first());
12925         }
12926         if (typeof(this.after) == 'object') {
12927             this.after.render(this.el.select('.roo-input-after',true).first());
12928         }
12929         
12930         this.inputEl().on('change', this.onChange, this);
12931         
12932     },
12933     filterValidation : function(e){
12934         if(!e.isNavKeyPress()){
12935             this.validationTask.delay(this.validationDelay);
12936         }
12937     },
12938      /**
12939      * Validates the field value
12940      * @return {Boolean} True if the value is valid, else false
12941      */
12942     validate : function(){
12943         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12944         if(this.disabled || this.validateValue(this.getRawValue())){
12945             this.markValid();
12946             return true;
12947         }
12948         
12949         this.markInvalid();
12950         return false;
12951     },
12952     
12953     
12954     /**
12955      * Validates a value according to the field's validation rules and marks the field as invalid
12956      * if the validation fails
12957      * @param {Mixed} value The value to validate
12958      * @return {Boolean} True if the value is valid, else false
12959      */
12960     validateValue : function(value)
12961     {
12962         if(this.getVisibilityEl().hasClass('hidden')){
12963             return true;
12964         }
12965         
12966         if(value.length < 1)  { // if it's blank
12967             if(this.allowBlank){
12968                 return true;
12969             }
12970             return false;
12971         }
12972         
12973         if(value.length < this.minLength){
12974             return false;
12975         }
12976         if(value.length > this.maxLength){
12977             return false;
12978         }
12979         if(this.vtype){
12980             var vt = Roo.form.VTypes;
12981             if(!vt[this.vtype](value, this)){
12982                 return false;
12983             }
12984         }
12985         if(typeof this.validator == "function"){
12986             var msg = this.validator(value);
12987             if (typeof(msg) == 'string') {
12988                 this.invalidText = msg;
12989             }
12990             if(msg !== true){
12991                 return false;
12992             }
12993         }
12994         
12995         if(this.regex && !this.regex.test(value)){
12996             return false;
12997         }
12998         
12999         return true;
13000     },
13001     
13002      // private
13003     fireKey : function(e){
13004         //Roo.log('field ' + e.getKey());
13005         if(e.isNavKeyPress()){
13006             this.fireEvent("specialkey", this, e);
13007         }
13008     },
13009     focus : function (selectText){
13010         if(this.rendered){
13011             this.inputEl().focus();
13012             if(selectText === true){
13013                 this.inputEl().dom.select();
13014             }
13015         }
13016         return this;
13017     } ,
13018     
13019     onFocus : function(){
13020         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13021            // this.el.addClass(this.focusClass);
13022         }
13023         if(!this.hasFocus){
13024             this.hasFocus = true;
13025             this.startValue = this.getValue();
13026             this.fireEvent("focus", this);
13027         }
13028     },
13029     
13030     beforeBlur : Roo.emptyFn,
13031
13032     
13033     // private
13034     onBlur : function(){
13035         this.beforeBlur();
13036         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13037             //this.el.removeClass(this.focusClass);
13038         }
13039         this.hasFocus = false;
13040         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13041             this.validate();
13042         }
13043         var v = this.getValue();
13044         if(String(v) !== String(this.startValue)){
13045             this.fireEvent('change', this, v, this.startValue);
13046         }
13047         this.fireEvent("blur", this);
13048     },
13049     
13050     onChange : function(e)
13051     {
13052         var v = this.getValue();
13053         if(String(v) !== String(this.startValue)){
13054             this.fireEvent('change', this, v, this.startValue);
13055         }
13056         
13057     },
13058     
13059     /**
13060      * Resets the current field value to the originally loaded value and clears any validation messages
13061      */
13062     reset : function(){
13063         this.setValue(this.originalValue);
13064         this.validate();
13065     },
13066      /**
13067      * Returns the name of the field
13068      * @return {Mixed} name The name field
13069      */
13070     getName: function(){
13071         return this.name;
13072     },
13073      /**
13074      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13075      * @return {Mixed} value The field value
13076      */
13077     getValue : function(){
13078         
13079         var v = this.inputEl().getValue();
13080         
13081         return v;
13082     },
13083     /**
13084      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13085      * @return {Mixed} value The field value
13086      */
13087     getRawValue : function(){
13088         var v = this.inputEl().getValue();
13089         
13090         return v;
13091     },
13092     
13093     /**
13094      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13095      * @param {Mixed} value The value to set
13096      */
13097     setRawValue : function(v){
13098         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13099     },
13100     
13101     selectText : function(start, end){
13102         var v = this.getRawValue();
13103         if(v.length > 0){
13104             start = start === undefined ? 0 : start;
13105             end = end === undefined ? v.length : end;
13106             var d = this.inputEl().dom;
13107             if(d.setSelectionRange){
13108                 d.setSelectionRange(start, end);
13109             }else if(d.createTextRange){
13110                 var range = d.createTextRange();
13111                 range.moveStart("character", start);
13112                 range.moveEnd("character", v.length-end);
13113                 range.select();
13114             }
13115         }
13116     },
13117     
13118     /**
13119      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13120      * @param {Mixed} value The value to set
13121      */
13122     setValue : function(v){
13123         this.value = v;
13124         if(this.rendered){
13125             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13126             this.validate();
13127         }
13128     },
13129     
13130     /*
13131     processValue : function(value){
13132         if(this.stripCharsRe){
13133             var newValue = value.replace(this.stripCharsRe, '');
13134             if(newValue !== value){
13135                 this.setRawValue(newValue);
13136                 return newValue;
13137             }
13138         }
13139         return value;
13140     },
13141   */
13142     preFocus : function(){
13143         
13144         if(this.selectOnFocus){
13145             this.inputEl().dom.select();
13146         }
13147     },
13148     filterKeys : function(e){
13149         var k = e.getKey();
13150         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13151             return;
13152         }
13153         var c = e.getCharCode(), cc = String.fromCharCode(c);
13154         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13155             return;
13156         }
13157         if(!this.maskRe.test(cc)){
13158             e.stopEvent();
13159         }
13160     },
13161      /**
13162      * Clear any invalid styles/messages for this field
13163      */
13164     clearInvalid : function(){
13165         
13166         if(!this.el || this.preventMark){ // not rendered
13167             return;
13168         }
13169         
13170         
13171         this.el.removeClass([this.invalidClass, 'is-invalid']);
13172         
13173         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13174             
13175             var feedback = this.el.select('.form-control-feedback', true).first();
13176             
13177             if(feedback){
13178                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13179             }
13180             
13181         }
13182         
13183         if(this.indicator){
13184             this.indicator.removeClass('visible');
13185             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13186         }
13187         
13188         this.fireEvent('valid', this);
13189     },
13190     
13191      /**
13192      * Mark this field as valid
13193      */
13194     markValid : function()
13195     {
13196         if(!this.el  || this.preventMark){ // not rendered...
13197             return;
13198         }
13199         
13200         this.el.removeClass([this.invalidClass, this.validClass]);
13201         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13202
13203         var feedback = this.el.select('.form-control-feedback', true).first();
13204             
13205         if(feedback){
13206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13207         }
13208         
13209         if(this.indicator){
13210             this.indicator.removeClass('visible');
13211             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13212         }
13213         
13214         if(this.disabled){
13215             return;
13216         }
13217         
13218            
13219         if(this.allowBlank && !this.getRawValue().length){
13220             return;
13221         }
13222         if (Roo.bootstrap.version == 3) {
13223             this.el.addClass(this.validClass);
13224         } else {
13225             this.inputEl().addClass('is-valid');
13226         }
13227
13228         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13229             
13230             var feedback = this.el.select('.form-control-feedback', true).first();
13231             
13232             if(feedback){
13233                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13234                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13235             }
13236             
13237         }
13238         
13239         this.fireEvent('valid', this);
13240     },
13241     
13242      /**
13243      * Mark this field as invalid
13244      * @param {String} msg The validation message
13245      */
13246     markInvalid : function(msg)
13247     {
13248         if(!this.el  || this.preventMark){ // not rendered
13249             return;
13250         }
13251         
13252         this.el.removeClass([this.invalidClass, this.validClass]);
13253         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13254         
13255         var feedback = this.el.select('.form-control-feedback', true).first();
13256             
13257         if(feedback){
13258             this.el.select('.form-control-feedback', true).first().removeClass(
13259                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13260         }
13261
13262         if(this.disabled){
13263             return;
13264         }
13265         
13266         if(this.allowBlank && !this.getRawValue().length){
13267             return;
13268         }
13269         
13270         if(this.indicator){
13271             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13272             this.indicator.addClass('visible');
13273         }
13274         if (Roo.bootstrap.version == 3) {
13275             this.el.addClass(this.invalidClass);
13276         } else {
13277             this.inputEl().addClass('is-invalid');
13278         }
13279         
13280         
13281         
13282         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13283             
13284             var feedback = this.el.select('.form-control-feedback', true).first();
13285             
13286             if(feedback){
13287                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13288                 
13289                 if(this.getValue().length || this.forceFeedback){
13290                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13291                 }
13292                 
13293             }
13294             
13295         }
13296         
13297         this.fireEvent('invalid', this, msg);
13298     },
13299     // private
13300     SafariOnKeyDown : function(event)
13301     {
13302         // this is a workaround for a password hang bug on chrome/ webkit.
13303         if (this.inputEl().dom.type != 'password') {
13304             return;
13305         }
13306         
13307         var isSelectAll = false;
13308         
13309         if(this.inputEl().dom.selectionEnd > 0){
13310             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13311         }
13312         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13313             event.preventDefault();
13314             this.setValue('');
13315             return;
13316         }
13317         
13318         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13319             
13320             event.preventDefault();
13321             // this is very hacky as keydown always get's upper case.
13322             //
13323             var cc = String.fromCharCode(event.getCharCode());
13324             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13325             
13326         }
13327     },
13328     adjustWidth : function(tag, w){
13329         tag = tag.toLowerCase();
13330         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13331             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13332                 if(tag == 'input'){
13333                     return w + 2;
13334                 }
13335                 if(tag == 'textarea'){
13336                     return w-2;
13337                 }
13338             }else if(Roo.isOpera){
13339                 if(tag == 'input'){
13340                     return w + 2;
13341                 }
13342                 if(tag == 'textarea'){
13343                     return w-2;
13344                 }
13345             }
13346         }
13347         return w;
13348     },
13349     
13350     setFieldLabel : function(v)
13351     {
13352         if(!this.rendered){
13353             return;
13354         }
13355         
13356         if(this.indicatorEl()){
13357             var ar = this.el.select('label > span',true);
13358             
13359             if (ar.elements.length) {
13360                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13361                 this.fieldLabel = v;
13362                 return;
13363             }
13364             
13365             var br = this.el.select('label',true);
13366             
13367             if(br.elements.length) {
13368                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13369                 this.fieldLabel = v;
13370                 return;
13371             }
13372             
13373             Roo.log('Cannot Found any of label > span || label in input');
13374             return;
13375         }
13376         
13377         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13378         this.fieldLabel = v;
13379         
13380         
13381     }
13382 });
13383
13384  
13385 /*
13386  * - LGPL
13387  *
13388  * Input
13389  * 
13390  */
13391
13392 /**
13393  * @class Roo.bootstrap.form.TextArea
13394  * @extends Roo.bootstrap.form.Input
13395  * Bootstrap TextArea class
13396  * @cfg {Number} cols Specifies the visible width of a text area
13397  * @cfg {Number} rows Specifies the visible number of lines in a text area
13398  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13399  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13400  * @cfg {string} html text
13401  * 
13402  * @constructor
13403  * Create a new TextArea
13404  * @param {Object} config The config object
13405  */
13406
13407 Roo.bootstrap.form.TextArea = function(config){
13408     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13409    
13410 };
13411
13412 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13413      
13414     cols : false,
13415     rows : 5,
13416     readOnly : false,
13417     warp : 'soft',
13418     resize : false,
13419     value: false,
13420     html: false,
13421     
13422     getAutoCreate : function(){
13423         
13424         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13425         
13426         var id = Roo.id();
13427         
13428         var cfg = {};
13429         
13430         if(this.inputType != 'hidden'){
13431             cfg.cls = 'form-group' //input-group
13432         }
13433         
13434         var input =  {
13435             tag: 'textarea',
13436             id : id,
13437             warp : this.warp,
13438             rows : this.rows,
13439             value : this.value || '',
13440             html: this.html || '',
13441             cls : 'form-control',
13442             placeholder : this.placeholder || '' 
13443             
13444         };
13445         
13446         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13447             input.maxLength = this.maxLength;
13448         }
13449         
13450         if(this.resize){
13451             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13452         }
13453         
13454         if(this.cols){
13455             input.cols = this.cols;
13456         }
13457         
13458         if (this.readOnly) {
13459             input.readonly = true;
13460         }
13461         
13462         if (this.name) {
13463             input.name = this.name;
13464         }
13465         
13466         if (this.size) {
13467             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13468         }
13469         
13470         var settings=this;
13471         ['xs','sm','md','lg'].map(function(size){
13472             if (settings[size]) {
13473                 cfg.cls += ' col-' + size + '-' + settings[size];
13474             }
13475         });
13476         
13477         var inputblock = input;
13478         
13479         if(this.hasFeedback && !this.allowBlank){
13480             
13481             var feedback = {
13482                 tag: 'span',
13483                 cls: 'glyphicon form-control-feedback'
13484             };
13485
13486             inputblock = {
13487                 cls : 'has-feedback',
13488                 cn :  [
13489                     input,
13490                     feedback
13491                 ] 
13492             };  
13493         }
13494         
13495         
13496         if (this.before || this.after) {
13497             
13498             inputblock = {
13499                 cls : 'input-group',
13500                 cn :  [] 
13501             };
13502             if (this.before) {
13503                 inputblock.cn.push({
13504                     tag :'span',
13505                     cls : 'input-group-addon',
13506                     html : this.before
13507                 });
13508             }
13509             
13510             inputblock.cn.push(input);
13511             
13512             if(this.hasFeedback && !this.allowBlank){
13513                 inputblock.cls += ' has-feedback';
13514                 inputblock.cn.push(feedback);
13515             }
13516             
13517             if (this.after) {
13518                 inputblock.cn.push({
13519                     tag :'span',
13520                     cls : 'input-group-addon',
13521                     html : this.after
13522                 });
13523             }
13524             
13525         }
13526         
13527         if (align ==='left' && this.fieldLabel.length) {
13528             cfg.cn = [
13529                 {
13530                     tag: 'label',
13531                     'for' :  id,
13532                     cls : 'control-label',
13533                     html : this.fieldLabel
13534                 },
13535                 {
13536                     cls : "",
13537                     cn: [
13538                         inputblock
13539                     ]
13540                 }
13541
13542             ];
13543             
13544             if(this.labelWidth > 12){
13545                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13546             }
13547
13548             if(this.labelWidth < 13 && this.labelmd == 0){
13549                 this.labelmd = this.labelWidth;
13550             }
13551
13552             if(this.labellg > 0){
13553                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13554                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13555             }
13556
13557             if(this.labelmd > 0){
13558                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13559                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13560             }
13561
13562             if(this.labelsm > 0){
13563                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13564                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13565             }
13566
13567             if(this.labelxs > 0){
13568                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13569                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13570             }
13571             
13572         } else if ( this.fieldLabel.length) {
13573             cfg.cn = [
13574
13575                {
13576                    tag: 'label',
13577                    //cls : 'input-group-addon',
13578                    html : this.fieldLabel
13579
13580                },
13581
13582                inputblock
13583
13584            ];
13585
13586         } else {
13587
13588             cfg.cn = [
13589
13590                 inputblock
13591
13592             ];
13593                 
13594         }
13595         
13596         if (this.disabled) {
13597             input.disabled=true;
13598         }
13599         
13600         return cfg;
13601         
13602     },
13603     /**
13604      * return the real textarea element.
13605      */
13606     inputEl: function ()
13607     {
13608         return this.el.select('textarea.form-control',true).first();
13609     },
13610     
13611     /**
13612      * Clear any invalid styles/messages for this field
13613      */
13614     clearInvalid : function()
13615     {
13616         
13617         if(!this.el || this.preventMark){ // not rendered
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         this.el.removeClass( this.validClass);
13628         this.inputEl().removeClass('is-invalid');
13629          
13630         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13631             
13632             var feedback = this.el.select('.form-control-feedback', true).first();
13633             
13634             if(feedback){
13635                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13636             }
13637             
13638         }
13639         
13640         this.fireEvent('valid', this);
13641     },
13642     
13643      /**
13644      * Mark this field as valid
13645      */
13646     markValid : function()
13647     {
13648         if(!this.el  || this.preventMark){ // not rendered
13649             return;
13650         }
13651         
13652         this.el.removeClass([this.invalidClass, this.validClass]);
13653         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13654         
13655         var feedback = this.el.select('.form-control-feedback', true).first();
13656             
13657         if(feedback){
13658             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13659         }
13660
13661         if(this.disabled || this.allowBlank){
13662             return;
13663         }
13664         
13665         var label = this.el.select('label', true).first();
13666         var icon = this.el.select('i.fa-star', true).first();
13667         
13668         if(label && icon){
13669             icon.remove();
13670         }
13671         if (Roo.bootstrap.version == 3) {
13672             this.el.addClass(this.validClass);
13673         } else {
13674             this.inputEl().addClass('is-valid');
13675         }
13676         
13677         
13678         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13679             
13680             var feedback = this.el.select('.form-control-feedback', true).first();
13681             
13682             if(feedback){
13683                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13684                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13685             }
13686             
13687         }
13688         
13689         this.fireEvent('valid', this);
13690     },
13691     
13692      /**
13693      * Mark this field as invalid
13694      * @param {String} msg The validation message
13695      */
13696     markInvalid : function(msg)
13697     {
13698         if(!this.el  || this.preventMark){ // not rendered
13699             return;
13700         }
13701         
13702         this.el.removeClass([this.invalidClass, this.validClass]);
13703         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13704         
13705         var feedback = this.el.select('.form-control-feedback', true).first();
13706             
13707         if(feedback){
13708             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13709         }
13710
13711         if(this.disabled || this.allowBlank){
13712             return;
13713         }
13714         
13715         var label = this.el.select('label', true).first();
13716         var icon = this.el.select('i.fa-star', true).first();
13717         
13718         if(!this.getValue().length && label && !icon){
13719             this.el.createChild({
13720                 tag : 'i',
13721                 cls : 'text-danger fa fa-lg fa-star',
13722                 tooltip : 'This field is required',
13723                 style : 'margin-right:5px;'
13724             }, label, true);
13725         }
13726         
13727         if (Roo.bootstrap.version == 3) {
13728             this.el.addClass(this.invalidClass);
13729         } else {
13730             this.inputEl().addClass('is-invalid');
13731         }
13732         
13733         // fixme ... this may be depricated need to test..
13734         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13735             
13736             var feedback = this.el.select('.form-control-feedback', true).first();
13737             
13738             if(feedback){
13739                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13740                 
13741                 if(this.getValue().length || this.forceFeedback){
13742                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13743                 }
13744                 
13745             }
13746             
13747         }
13748         
13749         this.fireEvent('invalid', this, msg);
13750     }
13751 });
13752
13753  
13754 /*
13755  * - LGPL
13756  *
13757  * trigger field - base class for combo..
13758  * 
13759  */
13760  
13761 /**
13762  * @class Roo.bootstrap.form.TriggerField
13763  * @extends Roo.bootstrap.form.Input
13764  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13765  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13766  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13767  * for which you can provide a custom implementation.  For example:
13768  * <pre><code>
13769 var trigger = new Roo.bootstrap.form.TriggerField();
13770 trigger.onTriggerClick = myTriggerFn;
13771 trigger.applyTo('my-field');
13772 </code></pre>
13773  *
13774  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13775  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13776  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13777  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13778  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13779
13780  * @constructor
13781  * Create a new TriggerField.
13782  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13783  * to the base TextField)
13784  */
13785 Roo.bootstrap.form.TriggerField = function(config){
13786     this.mimicing = false;
13787     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13788 };
13789
13790 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13791     /**
13792      * @cfg {String} triggerClass A CSS class to apply to the trigger
13793      */
13794      /**
13795      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13796      */
13797     hideTrigger:false,
13798
13799     /**
13800      * @cfg {Boolean} removable (true|false) special filter default false
13801      */
13802     removable : false,
13803     
13804     /** @cfg {Boolean} grow @hide */
13805     /** @cfg {Number} growMin @hide */
13806     /** @cfg {Number} growMax @hide */
13807
13808     /**
13809      * @hide 
13810      * @method
13811      */
13812     autoSize: Roo.emptyFn,
13813     // private
13814     monitorTab : true,
13815     // private
13816     deferHeight : true,
13817
13818     
13819     actionMode : 'wrap',
13820     
13821     caret : false,
13822     
13823     
13824     getAutoCreate : function(){
13825        
13826         var align = this.labelAlign || this.parentLabelAlign();
13827         
13828         var id = Roo.id();
13829         
13830         var cfg = {
13831             cls: 'form-group' //input-group
13832         };
13833         
13834         
13835         var input =  {
13836             tag: 'input',
13837             id : id,
13838             type : this.inputType,
13839             cls : 'form-control',
13840             autocomplete: 'new-password',
13841             placeholder : this.placeholder || '' 
13842             
13843         };
13844         if (this.name) {
13845             input.name = this.name;
13846         }
13847         if (this.size) {
13848             input.cls += ' input-' + this.size;
13849         }
13850         
13851         if (this.disabled) {
13852             input.disabled=true;
13853         }
13854         
13855         var inputblock = input;
13856         
13857         if(this.hasFeedback && !this.allowBlank){
13858             
13859             var feedback = {
13860                 tag: 'span',
13861                 cls: 'glyphicon form-control-feedback'
13862             };
13863             
13864             if(this.removable && !this.editable  ){
13865                 inputblock = {
13866                     cls : 'has-feedback',
13867                     cn :  [
13868                         inputblock,
13869                         {
13870                             tag: 'button',
13871                             html : 'x',
13872                             cls : 'roo-combo-removable-btn close'
13873                         },
13874                         feedback
13875                     ] 
13876                 };
13877             } else {
13878                 inputblock = {
13879                     cls : 'has-feedback',
13880                     cn :  [
13881                         inputblock,
13882                         feedback
13883                     ] 
13884                 };
13885             }
13886
13887         } else {
13888             if(this.removable && !this.editable ){
13889                 inputblock = {
13890                     cls : 'roo-removable',
13891                     cn :  [
13892                         inputblock,
13893                         {
13894                             tag: 'button',
13895                             html : 'x',
13896                             cls : 'roo-combo-removable-btn close'
13897                         }
13898                     ] 
13899                 };
13900             }
13901         }
13902         
13903         if (this.before || this.after) {
13904             
13905             inputblock = {
13906                 cls : 'input-group',
13907                 cn :  [] 
13908             };
13909             if (this.before) {
13910                 inputblock.cn.push({
13911                     tag :'span',
13912                     cls : 'input-group-addon input-group-prepend input-group-text',
13913                     html : this.before
13914                 });
13915             }
13916             
13917             inputblock.cn.push(input);
13918             
13919             if(this.hasFeedback && !this.allowBlank){
13920                 inputblock.cls += ' has-feedback';
13921                 inputblock.cn.push(feedback);
13922             }
13923             
13924             if (this.after) {
13925                 inputblock.cn.push({
13926                     tag :'span',
13927                     cls : 'input-group-addon input-group-append input-group-text',
13928                     html : this.after
13929                 });
13930             }
13931             
13932         };
13933         
13934       
13935         
13936         var ibwrap = inputblock;
13937         
13938         if(this.multiple){
13939             ibwrap = {
13940                 tag: 'ul',
13941                 cls: 'roo-select2-choices',
13942                 cn:[
13943                     {
13944                         tag: 'li',
13945                         cls: 'roo-select2-search-field',
13946                         cn: [
13947
13948                             inputblock
13949                         ]
13950                     }
13951                 ]
13952             };
13953                 
13954         }
13955         
13956         var combobox = {
13957             cls: 'roo-select2-container input-group',
13958             cn: [
13959                  {
13960                     tag: 'input',
13961                     type : 'hidden',
13962                     cls: 'form-hidden-field'
13963                 },
13964                 ibwrap
13965             ]
13966         };
13967         
13968         if(!this.multiple && this.showToggleBtn){
13969             
13970             var caret = {
13971                         tag: 'span',
13972                         cls: 'caret'
13973              };
13974             if (this.caret != false) {
13975                 caret = {
13976                      tag: 'i',
13977                      cls: 'fa fa-' + this.caret
13978                 };
13979                 
13980             }
13981             
13982             combobox.cn.push({
13983                 tag :'span',
13984                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13985                 cn : [
13986                     Roo.bootstrap.version == 3 ? caret : '',
13987                     {
13988                         tag: 'span',
13989                         cls: 'combobox-clear',
13990                         cn  : [
13991                             {
13992                                 tag : 'i',
13993                                 cls: 'icon-remove'
13994                             }
13995                         ]
13996                     }
13997                 ]
13998
13999             })
14000         }
14001         
14002         if(this.multiple){
14003             combobox.cls += ' roo-select2-container-multi';
14004         }
14005          var indicator = {
14006             tag : 'i',
14007             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14008             tooltip : 'This field is required'
14009         };
14010         if (Roo.bootstrap.version == 4) {
14011             indicator = {
14012                 tag : 'i',
14013                 style : 'display:none'
14014             };
14015         }
14016         
14017         
14018         if (align ==='left' && this.fieldLabel.length) {
14019             
14020             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14021
14022             cfg.cn = [
14023                 indicator,
14024                 {
14025                     tag: 'label',
14026                     'for' :  id,
14027                     cls : 'control-label',
14028                     html : this.fieldLabel
14029
14030                 },
14031                 {
14032                     cls : "", 
14033                     cn: [
14034                         combobox
14035                     ]
14036                 }
14037
14038             ];
14039             
14040             var labelCfg = cfg.cn[1];
14041             var contentCfg = cfg.cn[2];
14042             
14043             if(this.indicatorpos == 'right'){
14044                 cfg.cn = [
14045                     {
14046                         tag: 'label',
14047                         'for' :  id,
14048                         cls : 'control-label',
14049                         cn : [
14050                             {
14051                                 tag : 'span',
14052                                 html : this.fieldLabel
14053                             },
14054                             indicator
14055                         ]
14056                     },
14057                     {
14058                         cls : "", 
14059                         cn: [
14060                             combobox
14061                         ]
14062                     }
14063
14064                 ];
14065                 
14066                 labelCfg = cfg.cn[0];
14067                 contentCfg = cfg.cn[1];
14068             }
14069             
14070             if(this.labelWidth > 12){
14071                 labelCfg.style = "width: " + this.labelWidth + 'px';
14072             }
14073             
14074             if(this.labelWidth < 13 && this.labelmd == 0){
14075                 this.labelmd = this.labelWidth;
14076             }
14077             
14078             if(this.labellg > 0){
14079                 labelCfg.cls += ' col-lg-' + this.labellg;
14080                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14081             }
14082             
14083             if(this.labelmd > 0){
14084                 labelCfg.cls += ' col-md-' + this.labelmd;
14085                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14086             }
14087             
14088             if(this.labelsm > 0){
14089                 labelCfg.cls += ' col-sm-' + this.labelsm;
14090                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14091             }
14092             
14093             if(this.labelxs > 0){
14094                 labelCfg.cls += ' col-xs-' + this.labelxs;
14095                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14096             }
14097             
14098         } else if ( this.fieldLabel.length) {
14099 //                Roo.log(" label");
14100             cfg.cn = [
14101                 indicator,
14102                {
14103                    tag: 'label',
14104                    //cls : 'input-group-addon',
14105                    html : this.fieldLabel
14106
14107                },
14108
14109                combobox
14110
14111             ];
14112             
14113             if(this.indicatorpos == 'right'){
14114                 
14115                 cfg.cn = [
14116                     {
14117                        tag: 'label',
14118                        cn : [
14119                            {
14120                                tag : 'span',
14121                                html : this.fieldLabel
14122                            },
14123                            indicator
14124                        ]
14125
14126                     },
14127                     combobox
14128
14129                 ];
14130
14131             }
14132
14133         } else {
14134             
14135 //                Roo.log(" no label && no align");
14136                 cfg = combobox
14137                      
14138                 
14139         }
14140         
14141         var settings=this;
14142         ['xs','sm','md','lg'].map(function(size){
14143             if (settings[size]) {
14144                 cfg.cls += ' col-' + size + '-' + settings[size];
14145             }
14146         });
14147         
14148         return cfg;
14149         
14150     },
14151     
14152     
14153     
14154     // private
14155     onResize : function(w, h){
14156 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14157 //        if(typeof w == 'number'){
14158 //            var x = w - this.trigger.getWidth();
14159 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14160 //            this.trigger.setStyle('left', x+'px');
14161 //        }
14162     },
14163
14164     // private
14165     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14166
14167     // private
14168     getResizeEl : function(){
14169         return this.inputEl();
14170     },
14171
14172     // private
14173     getPositionEl : function(){
14174         return this.inputEl();
14175     },
14176
14177     // private
14178     alignErrorIcon : function(){
14179         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14180     },
14181
14182     // private
14183     initEvents : function(){
14184         
14185         this.createList();
14186         
14187         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14188         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14189         if(!this.multiple && this.showToggleBtn){
14190             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14191             if(this.hideTrigger){
14192                 this.trigger.setDisplayed(false);
14193             }
14194             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14195         }
14196         
14197         if(this.multiple){
14198             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14199         }
14200         
14201         if(this.removable && !this.editable && !this.tickable){
14202             var close = this.closeTriggerEl();
14203             
14204             if(close){
14205                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14206                 close.on('click', this.removeBtnClick, this, close);
14207             }
14208         }
14209         
14210         //this.trigger.addClassOnOver('x-form-trigger-over');
14211         //this.trigger.addClassOnClick('x-form-trigger-click');
14212         
14213         //if(!this.width){
14214         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14215         //}
14216     },
14217     
14218     closeTriggerEl : function()
14219     {
14220         var close = this.el.select('.roo-combo-removable-btn', true).first();
14221         return close ? close : false;
14222     },
14223     
14224     removeBtnClick : function(e, h, el)
14225     {
14226         e.preventDefault();
14227         
14228         if(this.fireEvent("remove", this) !== false){
14229             this.reset();
14230             this.fireEvent("afterremove", this)
14231         }
14232     },
14233     
14234     createList : function()
14235     {
14236         this.list = Roo.get(document.body).createChild({
14237             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14238             cls: 'typeahead typeahead-long dropdown-menu shadow',
14239             style: 'display:none'
14240         });
14241         
14242         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14243         
14244     },
14245
14246     // private
14247     initTrigger : function(){
14248        
14249     },
14250
14251     // private
14252     onDestroy : function(){
14253         if(this.trigger){
14254             this.trigger.removeAllListeners();
14255           //  this.trigger.remove();
14256         }
14257         //if(this.wrap){
14258         //    this.wrap.remove();
14259         //}
14260         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14261     },
14262
14263     // private
14264     onFocus : function(){
14265         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14266         /*
14267         if(!this.mimicing){
14268             this.wrap.addClass('x-trigger-wrap-focus');
14269             this.mimicing = true;
14270             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14271             if(this.monitorTab){
14272                 this.el.on("keydown", this.checkTab, this);
14273             }
14274         }
14275         */
14276     },
14277
14278     // private
14279     checkTab : function(e){
14280         if(e.getKey() == e.TAB){
14281             this.triggerBlur();
14282         }
14283     },
14284
14285     // private
14286     onBlur : function(){
14287         // do nothing
14288     },
14289
14290     // private
14291     mimicBlur : function(e, t){
14292         /*
14293         if(!this.wrap.contains(t) && this.validateBlur()){
14294             this.triggerBlur();
14295         }
14296         */
14297     },
14298
14299     // private
14300     triggerBlur : function(){
14301         this.mimicing = false;
14302         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14303         if(this.monitorTab){
14304             this.el.un("keydown", this.checkTab, this);
14305         }
14306         //this.wrap.removeClass('x-trigger-wrap-focus');
14307         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14308     },
14309
14310     // private
14311     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14312     validateBlur : function(e, t){
14313         return true;
14314     },
14315
14316     // private
14317     onDisable : function(){
14318         this.inputEl().dom.disabled = true;
14319         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14320         //if(this.wrap){
14321         //    this.wrap.addClass('x-item-disabled');
14322         //}
14323     },
14324
14325     // private
14326     onEnable : function(){
14327         this.inputEl().dom.disabled = false;
14328         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14329         //if(this.wrap){
14330         //    this.el.removeClass('x-item-disabled');
14331         //}
14332     },
14333
14334     // private
14335     onShow : function(){
14336         var ae = this.getActionEl();
14337         
14338         if(ae){
14339             ae.dom.style.display = '';
14340             ae.dom.style.visibility = 'visible';
14341         }
14342     },
14343
14344     // private
14345     
14346     onHide : function(){
14347         var ae = this.getActionEl();
14348         ae.dom.style.display = 'none';
14349     },
14350
14351     /**
14352      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14353      * by an implementing function.
14354      * @method
14355      * @param {EventObject} e
14356      */
14357     onTriggerClick : Roo.emptyFn
14358 });
14359  
14360 /*
14361 * Licence: LGPL
14362 */
14363
14364 /**
14365  * @class Roo.bootstrap.form.CardUploader
14366  * @extends Roo.bootstrap.Button
14367  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14368  * @cfg {Number} errorTimeout default 3000
14369  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14370  * @cfg {Array}  html The button text.
14371
14372  *
14373  * @constructor
14374  * Create a new CardUploader
14375  * @param {Object} config The config object
14376  */
14377
14378 Roo.bootstrap.form.CardUploader = function(config){
14379     
14380  
14381     
14382     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14383     
14384     
14385     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14386         return r.data.id
14387      });
14388     
14389      this.addEvents({
14390          // raw events
14391         /**
14392          * @event preview
14393          * When a image is clicked on - and needs to display a slideshow or similar..
14394          * @param {Roo.bootstrap.Card} this
14395          * @param {Object} The image information data 
14396          *
14397          */
14398         'preview' : true,
14399          /**
14400          * @event download
14401          * When a the download link is clicked
14402          * @param {Roo.bootstrap.Card} this
14403          * @param {Object} The image information data  contains 
14404          */
14405         'download' : true
14406         
14407     });
14408 };
14409  
14410 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14411     
14412      
14413     errorTimeout : 3000,
14414      
14415     images : false,
14416    
14417     fileCollection : false,
14418     allowBlank : true,
14419     
14420     getAutoCreate : function()
14421     {
14422         
14423         var cfg =  {
14424             cls :'form-group' ,
14425             cn : [
14426                
14427                 {
14428                     tag: 'label',
14429                    //cls : 'input-group-addon',
14430                     html : this.fieldLabel
14431
14432                 },
14433
14434                 {
14435                     tag: 'input',
14436                     type : 'hidden',
14437                     name : this.name,
14438                     value : this.value,
14439                     cls : 'd-none  form-control'
14440                 },
14441                 
14442                 {
14443                     tag: 'input',
14444                     multiple : 'multiple',
14445                     type : 'file',
14446                     cls : 'd-none  roo-card-upload-selector'
14447                 },
14448                 
14449                 {
14450                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14451                 },
14452                 {
14453                     cls : 'card-columns roo-card-uploader-container'
14454                 }
14455
14456             ]
14457         };
14458            
14459          
14460         return cfg;
14461     },
14462     
14463     getChildContainer : function() /// what children are added to.
14464     {
14465         return this.containerEl;
14466     },
14467    
14468     getButtonContainer : function() /// what children are added to.
14469     {
14470         return this.el.select(".roo-card-uploader-button-container").first();
14471     },
14472    
14473     initEvents : function()
14474     {
14475         
14476         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14477         
14478         var t = this;
14479         this.addxtype({
14480             xns: Roo.bootstrap,
14481
14482             xtype : 'Button',
14483             container_method : 'getButtonContainer' ,            
14484             html :  this.html, // fix changable?
14485             cls : 'w-100 ',
14486             listeners : {
14487                 'click' : function(btn, e) {
14488                     t.onClick(e);
14489                 }
14490             }
14491         });
14492         
14493         
14494         
14495         
14496         this.urlAPI = (window.createObjectURL && window) || 
14497                                 (window.URL && URL.revokeObjectURL && URL) || 
14498                                 (window.webkitURL && webkitURL);
14499                         
14500          
14501          
14502          
14503         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14504         
14505         this.selectorEl.on('change', this.onFileSelected, this);
14506         if (this.images) {
14507             var t = this;
14508             this.images.forEach(function(img) {
14509                 t.addCard(img)
14510             });
14511             this.images = false;
14512         }
14513         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14514          
14515        
14516     },
14517     
14518    
14519     onClick : function(e)
14520     {
14521         e.preventDefault();
14522          
14523         this.selectorEl.dom.click();
14524          
14525     },
14526     
14527     onFileSelected : function(e)
14528     {
14529         e.preventDefault();
14530         
14531         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14532             return;
14533         }
14534         
14535         Roo.each(this.selectorEl.dom.files, function(file){    
14536             this.addFile(file);
14537         }, this);
14538          
14539     },
14540     
14541       
14542     
14543       
14544     
14545     addFile : function(file)
14546     {
14547            
14548         if(typeof(file) === 'string'){
14549             throw "Add file by name?"; // should not happen
14550             return;
14551         }
14552         
14553         if(!file || !this.urlAPI){
14554             return;
14555         }
14556         
14557         // file;
14558         // file.type;
14559         
14560         var _this = this;
14561         
14562         
14563         var url = _this.urlAPI.createObjectURL( file);
14564            
14565         this.addCard({
14566             id : Roo.bootstrap.form.CardUploader.ID--,
14567             is_uploaded : false,
14568             src : url,
14569             srcfile : file,
14570             title : file.name,
14571             mimetype : file.type,
14572             preview : false,
14573             is_deleted : 0
14574         });
14575         
14576     },
14577     
14578     /**
14579      * addCard - add an Attachment to the uploader
14580      * @param data - the data about the image to upload
14581      *
14582      * {
14583           id : 123
14584           title : "Title of file",
14585           is_uploaded : false,
14586           src : "http://.....",
14587           srcfile : { the File upload object },
14588           mimetype : file.type,
14589           preview : false,
14590           is_deleted : 0
14591           .. any other data...
14592         }
14593      *
14594      * 
14595     */
14596     
14597     addCard : function (data)
14598     {
14599         // hidden input element?
14600         // if the file is not an image...
14601         //then we need to use something other that and header_image
14602         var t = this;
14603         //   remove.....
14604         var footer = [
14605             {
14606                 xns : Roo.bootstrap,
14607                 xtype : 'CardFooter',
14608                  items: [
14609                     {
14610                         xns : Roo.bootstrap,
14611                         xtype : 'Element',
14612                         cls : 'd-flex',
14613                         items : [
14614                             
14615                             {
14616                                 xns : Roo.bootstrap,
14617                                 xtype : 'Button',
14618                                 html : String.format("<small>{0}</small>", data.title),
14619                                 cls : 'col-10 text-left',
14620                                 size: 'sm',
14621                                 weight: 'link',
14622                                 fa : 'download',
14623                                 listeners : {
14624                                     click : function() {
14625                                      
14626                                         t.fireEvent( "download", t, data );
14627                                     }
14628                                 }
14629                             },
14630                           
14631                             {
14632                                 xns : Roo.bootstrap,
14633                                 xtype : 'Button',
14634                                 style: 'max-height: 28px; ',
14635                                 size : 'sm',
14636                                 weight: 'danger',
14637                                 cls : 'col-2',
14638                                 fa : 'times',
14639                                 listeners : {
14640                                     click : function() {
14641                                         t.removeCard(data.id)
14642                                     }
14643                                 }
14644                             }
14645                         ]
14646                     }
14647                     
14648                 ] 
14649             }
14650             
14651         ];
14652         
14653         var cn = this.addxtype(
14654             {
14655                  
14656                 xns : Roo.bootstrap,
14657                 xtype : 'Card',
14658                 closeable : true,
14659                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14660                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14661                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14662                 data : data,
14663                 html : false,
14664                  
14665                 items : footer,
14666                 initEvents : function() {
14667                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14668                     var card = this;
14669                     this.imgEl = this.el.select('.card-img-top').first();
14670                     if (this.imgEl) {
14671                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14672                         this.imgEl.set({ 'pointer' : 'cursor' });
14673                                   
14674                     }
14675                     this.getCardFooter().addClass('p-1');
14676                     
14677                   
14678                 }
14679                 
14680             }
14681         );
14682         // dont' really need ot update items.
14683         // this.items.push(cn);
14684         this.fileCollection.add(cn);
14685         
14686         if (!data.srcfile) {
14687             this.updateInput();
14688             return;
14689         }
14690             
14691         var _t = this;
14692         var reader = new FileReader();
14693         reader.addEventListener("load", function() {  
14694             data.srcdata =  reader.result;
14695             _t.updateInput();
14696         });
14697         reader.readAsDataURL(data.srcfile);
14698         
14699         
14700         
14701     },
14702     removeCard : function(id)
14703     {
14704         
14705         var card  = this.fileCollection.get(id);
14706         card.data.is_deleted = 1;
14707         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14708         //this.fileCollection.remove(card);
14709         //this.items = this.items.filter(function(e) { return e != card });
14710         // dont' really need ot update items.
14711         card.el.dom.parentNode.removeChild(card.el.dom);
14712         this.updateInput();
14713
14714         
14715     },
14716     reset: function()
14717     {
14718         this.fileCollection.each(function(card) {
14719             if (card.el.dom && card.el.dom.parentNode) {
14720                 card.el.dom.parentNode.removeChild(card.el.dom);
14721             }
14722         });
14723         this.fileCollection.clear();
14724         this.updateInput();
14725     },
14726     
14727     updateInput : function()
14728     {
14729          var data = [];
14730         this.fileCollection.each(function(e) {
14731             data.push(e.data);
14732             
14733         });
14734         this.inputEl().dom.value = JSON.stringify(data);
14735         
14736         
14737         
14738     }
14739     
14740     
14741 });
14742
14743
14744 Roo.bootstrap.form.CardUploader.ID = -1;/*
14745  * Based on:
14746  * Ext JS Library 1.1.1
14747  * Copyright(c) 2006-2007, Ext JS, LLC.
14748  *
14749  * Originally Released Under LGPL - original licence link has changed is not relivant.
14750  *
14751  * Fork - LGPL
14752  * <script type="text/javascript">
14753  */
14754
14755
14756 /**
14757  * @class Roo.data.SortTypes
14758  * @static
14759  * Defines the default sorting (casting?) comparison functions used when sorting data.
14760  */
14761 Roo.data.SortTypes = {
14762     /**
14763      * Default sort that does nothing
14764      * @param {Mixed} s The value being converted
14765      * @return {Mixed} The comparison value
14766      */
14767     none : function(s){
14768         return s;
14769     },
14770     
14771     /**
14772      * The regular expression used to strip tags
14773      * @type {RegExp}
14774      * @property
14775      */
14776     stripTagsRE : /<\/?[^>]+>/gi,
14777     
14778     /**
14779      * Strips all HTML tags to sort on text only
14780      * @param {Mixed} s The value being converted
14781      * @return {String} The comparison value
14782      */
14783     asText : function(s){
14784         return String(s).replace(this.stripTagsRE, "");
14785     },
14786     
14787     /**
14788      * Strips all HTML tags to sort on text only - Case insensitive
14789      * @param {Mixed} s The value being converted
14790      * @return {String} The comparison value
14791      */
14792     asUCText : function(s){
14793         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14794     },
14795     
14796     /**
14797      * Case insensitive string
14798      * @param {Mixed} s The value being converted
14799      * @return {String} The comparison value
14800      */
14801     asUCString : function(s) {
14802         return String(s).toUpperCase();
14803     },
14804     
14805     /**
14806      * Date sorting
14807      * @param {Mixed} s The value being converted
14808      * @return {Number} The comparison value
14809      */
14810     asDate : function(s) {
14811         if(!s){
14812             return 0;
14813         }
14814         if(s instanceof Date){
14815             return s.getTime();
14816         }
14817         return Date.parse(String(s));
14818     },
14819     
14820     /**
14821      * Float sorting
14822      * @param {Mixed} s The value being converted
14823      * @return {Float} The comparison value
14824      */
14825     asFloat : function(s) {
14826         var val = parseFloat(String(s).replace(/,/g, ""));
14827         if(isNaN(val)) {
14828             val = 0;
14829         }
14830         return val;
14831     },
14832     
14833     /**
14834      * Integer sorting
14835      * @param {Mixed} s The value being converted
14836      * @return {Number} The comparison value
14837      */
14838     asInt : function(s) {
14839         var val = parseInt(String(s).replace(/,/g, ""));
14840         if(isNaN(val)) {
14841             val = 0;
14842         }
14843         return val;
14844     }
14845 };/*
14846  * Based on:
14847  * Ext JS Library 1.1.1
14848  * Copyright(c) 2006-2007, Ext JS, LLC.
14849  *
14850  * Originally Released Under LGPL - original licence link has changed is not relivant.
14851  *
14852  * Fork - LGPL
14853  * <script type="text/javascript">
14854  */
14855
14856 /**
14857 * @class Roo.data.Record
14858  * Instances of this class encapsulate both record <em>definition</em> information, and record
14859  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14860  * to access Records cached in an {@link Roo.data.Store} object.<br>
14861  * <p>
14862  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14863  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14864  * objects.<br>
14865  * <p>
14866  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14867  * @constructor
14868  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14869  * {@link #create}. The parameters are the same.
14870  * @param {Array} data An associative Array of data values keyed by the field name.
14871  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14872  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14873  * not specified an integer id is generated.
14874  */
14875 Roo.data.Record = function(data, id){
14876     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14877     this.data = data;
14878 };
14879
14880 /**
14881  * Generate a constructor for a specific record layout.
14882  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14883  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14884  * Each field definition object may contain the following properties: <ul>
14885  * <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,
14886  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14887  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14888  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14889  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14890  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14891  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14892  * this may be omitted.</p></li>
14893  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14894  * <ul><li>auto (Default, implies no conversion)</li>
14895  * <li>string</li>
14896  * <li>int</li>
14897  * <li>float</li>
14898  * <li>boolean</li>
14899  * <li>date</li></ul></p></li>
14900  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14901  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14902  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14903  * by the Reader into an object that will be stored in the Record. It is passed the
14904  * following parameters:<ul>
14905  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14906  * </ul></p></li>
14907  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14908  * </ul>
14909  * <br>usage:<br><pre><code>
14910 var TopicRecord = Roo.data.Record.create(
14911     {name: 'title', mapping: 'topic_title'},
14912     {name: 'author', mapping: 'username'},
14913     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14914     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14915     {name: 'lastPoster', mapping: 'user2'},
14916     {name: 'excerpt', mapping: 'post_text'}
14917 );
14918
14919 var myNewRecord = new TopicRecord({
14920     title: 'Do my job please',
14921     author: 'noobie',
14922     totalPosts: 1,
14923     lastPost: new Date(),
14924     lastPoster: 'Animal',
14925     excerpt: 'No way dude!'
14926 });
14927 myStore.add(myNewRecord);
14928 </code></pre>
14929  * @method create
14930  * @static
14931  */
14932 Roo.data.Record.create = function(o){
14933     var f = function(){
14934         f.superclass.constructor.apply(this, arguments);
14935     };
14936     Roo.extend(f, Roo.data.Record);
14937     var p = f.prototype;
14938     p.fields = new Roo.util.MixedCollection(false, function(field){
14939         return field.name;
14940     });
14941     for(var i = 0, len = o.length; i < len; i++){
14942         p.fields.add(new Roo.data.Field(o[i]));
14943     }
14944     f.getField = function(name){
14945         return p.fields.get(name);  
14946     };
14947     return f;
14948 };
14949
14950 Roo.data.Record.AUTO_ID = 1000;
14951 Roo.data.Record.EDIT = 'edit';
14952 Roo.data.Record.REJECT = 'reject';
14953 Roo.data.Record.COMMIT = 'commit';
14954
14955 Roo.data.Record.prototype = {
14956     /**
14957      * Readonly flag - true if this record has been modified.
14958      * @type Boolean
14959      */
14960     dirty : false,
14961     editing : false,
14962     error: null,
14963     modified: null,
14964
14965     // private
14966     join : function(store){
14967         this.store = store;
14968     },
14969
14970     /**
14971      * Set the named field to the specified value.
14972      * @param {String} name The name of the field to set.
14973      * @param {Object} value The value to set the field to.
14974      */
14975     set : function(name, value){
14976         if(this.data[name] == value){
14977             return;
14978         }
14979         this.dirty = true;
14980         if(!this.modified){
14981             this.modified = {};
14982         }
14983         if(typeof this.modified[name] == 'undefined'){
14984             this.modified[name] = this.data[name];
14985         }
14986         this.data[name] = value;
14987         if(!this.editing && this.store){
14988             this.store.afterEdit(this);
14989         }       
14990     },
14991
14992     /**
14993      * Get the value of the named field.
14994      * @param {String} name The name of the field to get the value of.
14995      * @return {Object} The value of the field.
14996      */
14997     get : function(name){
14998         return this.data[name]; 
14999     },
15000
15001     // private
15002     beginEdit : function(){
15003         this.editing = true;
15004         this.modified = {}; 
15005     },
15006
15007     // private
15008     cancelEdit : function(){
15009         this.editing = false;
15010         delete this.modified;
15011     },
15012
15013     // private
15014     endEdit : function(){
15015         this.editing = false;
15016         if(this.dirty && this.store){
15017             this.store.afterEdit(this);
15018         }
15019     },
15020
15021     /**
15022      * Usually called by the {@link Roo.data.Store} which owns the Record.
15023      * Rejects all changes made to the Record since either creation, or the last commit operation.
15024      * Modified fields are reverted to their original values.
15025      * <p>
15026      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15027      * of reject operations.
15028      */
15029     reject : function(){
15030         var m = this.modified;
15031         for(var n in m){
15032             if(typeof m[n] != "function"){
15033                 this.data[n] = m[n];
15034             }
15035         }
15036         this.dirty = false;
15037         delete this.modified;
15038         this.editing = false;
15039         if(this.store){
15040             this.store.afterReject(this);
15041         }
15042     },
15043
15044     /**
15045      * Usually called by the {@link Roo.data.Store} which owns the Record.
15046      * Commits all changes made to the Record since either creation, or the last commit operation.
15047      * <p>
15048      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15049      * of commit operations.
15050      */
15051     commit : function(){
15052         this.dirty = false;
15053         delete this.modified;
15054         this.editing = false;
15055         if(this.store){
15056             this.store.afterCommit(this);
15057         }
15058     },
15059
15060     // private
15061     hasError : function(){
15062         return this.error != null;
15063     },
15064
15065     // private
15066     clearError : function(){
15067         this.error = null;
15068     },
15069
15070     /**
15071      * Creates a copy of this record.
15072      * @param {String} id (optional) A new record id if you don't want to use this record's id
15073      * @return {Record}
15074      */
15075     copy : function(newId) {
15076         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15077     }
15078 };/*
15079  * Based on:
15080  * Ext JS Library 1.1.1
15081  * Copyright(c) 2006-2007, Ext JS, LLC.
15082  *
15083  * Originally Released Under LGPL - original licence link has changed is not relivant.
15084  *
15085  * Fork - LGPL
15086  * <script type="text/javascript">
15087  */
15088
15089
15090
15091 /**
15092  * @class Roo.data.Store
15093  * @extends Roo.util.Observable
15094  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15095  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15096  * <p>
15097  * 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
15098  * has no knowledge of the format of the data returned by the Proxy.<br>
15099  * <p>
15100  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15101  * instances from the data object. These records are cached and made available through accessor functions.
15102  * @constructor
15103  * Creates a new Store.
15104  * @param {Object} config A config object containing the objects needed for the Store to access data,
15105  * and read the data into Records.
15106  */
15107 Roo.data.Store = function(config){
15108     this.data = new Roo.util.MixedCollection(false);
15109     this.data.getKey = function(o){
15110         return o.id;
15111     };
15112     this.baseParams = {};
15113     // private
15114     this.paramNames = {
15115         "start" : "start",
15116         "limit" : "limit",
15117         "sort" : "sort",
15118         "dir" : "dir",
15119         "multisort" : "_multisort"
15120     };
15121
15122     if(config && config.data){
15123         this.inlineData = config.data;
15124         delete config.data;
15125     }
15126
15127     Roo.apply(this, config);
15128     
15129     if(this.reader){ // reader passed
15130         this.reader = Roo.factory(this.reader, Roo.data);
15131         this.reader.xmodule = this.xmodule || false;
15132         if(!this.recordType){
15133             this.recordType = this.reader.recordType;
15134         }
15135         if(this.reader.onMetaChange){
15136             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15137         }
15138     }
15139
15140     if(this.recordType){
15141         this.fields = this.recordType.prototype.fields;
15142     }
15143     this.modified = [];
15144
15145     this.addEvents({
15146         /**
15147          * @event datachanged
15148          * Fires when the data cache has changed, and a widget which is using this Store
15149          * as a Record cache should refresh its view.
15150          * @param {Store} this
15151          */
15152         datachanged : true,
15153         /**
15154          * @event metachange
15155          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15156          * @param {Store} this
15157          * @param {Object} meta The JSON metadata
15158          */
15159         metachange : true,
15160         /**
15161          * @event add
15162          * Fires when Records have been added to the Store
15163          * @param {Store} this
15164          * @param {Roo.data.Record[]} records The array of Records added
15165          * @param {Number} index The index at which the record(s) were added
15166          */
15167         add : true,
15168         /**
15169          * @event remove
15170          * Fires when a Record has been removed from the Store
15171          * @param {Store} this
15172          * @param {Roo.data.Record} record The Record that was removed
15173          * @param {Number} index The index at which the record was removed
15174          */
15175         remove : true,
15176         /**
15177          * @event update
15178          * Fires when a Record has been updated
15179          * @param {Store} this
15180          * @param {Roo.data.Record} record The Record that was updated
15181          * @param {String} operation The update operation being performed.  Value may be one of:
15182          * <pre><code>
15183  Roo.data.Record.EDIT
15184  Roo.data.Record.REJECT
15185  Roo.data.Record.COMMIT
15186          * </code></pre>
15187          */
15188         update : true,
15189         /**
15190          * @event clear
15191          * Fires when the data cache has been cleared.
15192          * @param {Store} this
15193          */
15194         clear : true,
15195         /**
15196          * @event beforeload
15197          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15198          * the load action will be canceled.
15199          * @param {Store} this
15200          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15201          */
15202         beforeload : true,
15203         /**
15204          * @event beforeloadadd
15205          * Fires after a new set of Records has been loaded.
15206          * @param {Store} this
15207          * @param {Roo.data.Record[]} records The Records that were loaded
15208          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15209          */
15210         beforeloadadd : true,
15211         /**
15212          * @event load
15213          * Fires after a new set of Records has been loaded, before they are added to the store.
15214          * @param {Store} this
15215          * @param {Roo.data.Record[]} records The Records that were loaded
15216          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15217          * @params {Object} return from reader
15218          */
15219         load : true,
15220         /**
15221          * @event loadexception
15222          * Fires if an exception occurs in the Proxy during loading.
15223          * Called with the signature of the Proxy's "loadexception" event.
15224          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15225          * 
15226          * @param {Proxy} 
15227          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15228          * @param {Object} load options 
15229          * @param {Object} jsonData from your request (normally this contains the Exception)
15230          */
15231         loadexception : true
15232     });
15233     
15234     if(this.proxy){
15235         this.proxy = Roo.factory(this.proxy, Roo.data);
15236         this.proxy.xmodule = this.xmodule || false;
15237         this.relayEvents(this.proxy,  ["loadexception"]);
15238     }
15239     this.sortToggle = {};
15240     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15241
15242     Roo.data.Store.superclass.constructor.call(this);
15243
15244     if(this.inlineData){
15245         this.loadData(this.inlineData);
15246         delete this.inlineData;
15247     }
15248 };
15249
15250 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15251      /**
15252     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15253     * without a remote query - used by combo/forms at present.
15254     */
15255     
15256     /**
15257     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15258     */
15259     /**
15260     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15261     */
15262     /**
15263     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15264     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15265     */
15266     /**
15267     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15268     * on any HTTP request
15269     */
15270     /**
15271     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15272     */
15273     /**
15274     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15275     */
15276     multiSort: false,
15277     /**
15278     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15279     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15280     */
15281     remoteSort : false,
15282
15283     /**
15284     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15285      * loaded or when a record is removed. (defaults to false).
15286     */
15287     pruneModifiedRecords : false,
15288
15289     // private
15290     lastOptions : null,
15291
15292     /**
15293      * Add Records to the Store and fires the add event.
15294      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15295      */
15296     add : function(records){
15297         records = [].concat(records);
15298         for(var i = 0, len = records.length; i < len; i++){
15299             records[i].join(this);
15300         }
15301         var index = this.data.length;
15302         this.data.addAll(records);
15303         this.fireEvent("add", this, records, index);
15304     },
15305
15306     /**
15307      * Remove a Record from the Store and fires the remove event.
15308      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15309      */
15310     remove : function(record){
15311         var index = this.data.indexOf(record);
15312         this.data.removeAt(index);
15313  
15314         if(this.pruneModifiedRecords){
15315             this.modified.remove(record);
15316         }
15317         this.fireEvent("remove", this, record, index);
15318     },
15319
15320     /**
15321      * Remove all Records from the Store and fires the clear event.
15322      */
15323     removeAll : function(){
15324         this.data.clear();
15325         if(this.pruneModifiedRecords){
15326             this.modified = [];
15327         }
15328         this.fireEvent("clear", this);
15329     },
15330
15331     /**
15332      * Inserts Records to the Store at the given index and fires the add event.
15333      * @param {Number} index The start index at which to insert the passed Records.
15334      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15335      */
15336     insert : function(index, records){
15337         records = [].concat(records);
15338         for(var i = 0, len = records.length; i < len; i++){
15339             this.data.insert(index, records[i]);
15340             records[i].join(this);
15341         }
15342         this.fireEvent("add", this, records, index);
15343     },
15344
15345     /**
15346      * Get the index within the cache of the passed Record.
15347      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15348      * @return {Number} The index of the passed Record. Returns -1 if not found.
15349      */
15350     indexOf : function(record){
15351         return this.data.indexOf(record);
15352     },
15353
15354     /**
15355      * Get the index within the cache of the Record with the passed id.
15356      * @param {String} id The id of the Record to find.
15357      * @return {Number} The index of the Record. Returns -1 if not found.
15358      */
15359     indexOfId : function(id){
15360         return this.data.indexOfKey(id);
15361     },
15362
15363     /**
15364      * Get the Record with the specified id.
15365      * @param {String} id The id of the Record to find.
15366      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15367      */
15368     getById : function(id){
15369         return this.data.key(id);
15370     },
15371
15372     /**
15373      * Get the Record at the specified index.
15374      * @param {Number} index The index of the Record to find.
15375      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15376      */
15377     getAt : function(index){
15378         return this.data.itemAt(index);
15379     },
15380
15381     /**
15382      * Returns a range of Records between specified indices.
15383      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15384      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15385      * @return {Roo.data.Record[]} An array of Records
15386      */
15387     getRange : function(start, end){
15388         return this.data.getRange(start, end);
15389     },
15390
15391     // private
15392     storeOptions : function(o){
15393         o = Roo.apply({}, o);
15394         delete o.callback;
15395         delete o.scope;
15396         this.lastOptions = o;
15397     },
15398
15399     /**
15400      * Loads the Record cache from the configured Proxy using the configured Reader.
15401      * <p>
15402      * If using remote paging, then the first load call must specify the <em>start</em>
15403      * and <em>limit</em> properties in the options.params property to establish the initial
15404      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15405      * <p>
15406      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15407      * and this call will return before the new data has been loaded. Perform any post-processing
15408      * in a callback function, or in a "load" event handler.</strong>
15409      * <p>
15410      * @param {Object} options An object containing properties which control loading options:<ul>
15411      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15412      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15413      * <pre>
15414                 {
15415                     data : data,  // array of key=>value data like JsonReader
15416                     total : data.length,
15417                     success : true
15418                     
15419                 }
15420         </pre>
15421             }.</li>
15422      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15423      * passed the following arguments:<ul>
15424      * <li>r : Roo.data.Record[]</li>
15425      * <li>options: Options object from the load call</li>
15426      * <li>success: Boolean success indicator</li></ul></li>
15427      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15428      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15429      * </ul>
15430      */
15431     load : function(options){
15432         options = options || {};
15433         if(this.fireEvent("beforeload", this, options) !== false){
15434             this.storeOptions(options);
15435             var p = Roo.apply(options.params || {}, this.baseParams);
15436             // if meta was not loaded from remote source.. try requesting it.
15437             if (!this.reader.metaFromRemote) {
15438                 p._requestMeta = 1;
15439             }
15440             if(this.sortInfo && this.remoteSort){
15441                 var pn = this.paramNames;
15442                 p[pn["sort"]] = this.sortInfo.field;
15443                 p[pn["dir"]] = this.sortInfo.direction;
15444             }
15445             if (this.multiSort) {
15446                 var pn = this.paramNames;
15447                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15448             }
15449             
15450             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15451         }
15452     },
15453
15454     /**
15455      * Reloads the Record cache from the configured Proxy using the configured Reader and
15456      * the options from the last load operation performed.
15457      * @param {Object} options (optional) An object containing properties which may override the options
15458      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15459      * the most recently used options are reused).
15460      */
15461     reload : function(options){
15462         this.load(Roo.applyIf(options||{}, this.lastOptions));
15463     },
15464
15465     // private
15466     // Called as a callback by the Reader during a load operation.
15467     loadRecords : function(o, options, success){
15468          
15469         if(!o){
15470             if(success !== false){
15471                 this.fireEvent("load", this, [], options, o);
15472             }
15473             if(options.callback){
15474                 options.callback.call(options.scope || this, [], options, false);
15475             }
15476             return;
15477         }
15478         // if data returned failure - throw an exception.
15479         if (o.success === false) {
15480             // show a message if no listener is registered.
15481             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15482                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15483             }
15484             // loadmask wil be hooked into this..
15485             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15486             return;
15487         }
15488         var r = o.records, t = o.totalRecords || r.length;
15489         
15490         this.fireEvent("beforeloadadd", this, r, options, o);
15491         
15492         if(!options || options.add !== true){
15493             if(this.pruneModifiedRecords){
15494                 this.modified = [];
15495             }
15496             for(var i = 0, len = r.length; i < len; i++){
15497                 r[i].join(this);
15498             }
15499             if(this.snapshot){
15500                 this.data = this.snapshot;
15501                 delete this.snapshot;
15502             }
15503             this.data.clear();
15504             this.data.addAll(r);
15505             this.totalLength = t;
15506             this.applySort();
15507             this.fireEvent("datachanged", this);
15508         }else{
15509             this.totalLength = Math.max(t, this.data.length+r.length);
15510             this.add(r);
15511         }
15512         
15513         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15514                 
15515             var e = new Roo.data.Record({});
15516
15517             e.set(this.parent.displayField, this.parent.emptyTitle);
15518             e.set(this.parent.valueField, '');
15519
15520             this.insert(0, e);
15521         }
15522             
15523         this.fireEvent("load", this, r, options, o);
15524         if(options.callback){
15525             options.callback.call(options.scope || this, r, options, true);
15526         }
15527     },
15528
15529
15530     /**
15531      * Loads data from a passed data block. A Reader which understands the format of the data
15532      * must have been configured in the constructor.
15533      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15534      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15535      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15536      */
15537     loadData : function(o, append){
15538         var r = this.reader.readRecords(o);
15539         this.loadRecords(r, {add: append}, true);
15540     },
15541     
15542      /**
15543      * using 'cn' the nested child reader read the child array into it's child stores.
15544      * @param {Object} rec The record with a 'children array
15545      */
15546     loadDataFromChildren : function(rec)
15547     {
15548         this.loadData(this.reader.toLoadData(rec));
15549     },
15550     
15551
15552     /**
15553      * Gets the number of cached records.
15554      * <p>
15555      * <em>If using paging, this may not be the total size of the dataset. If the data object
15556      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15557      * the data set size</em>
15558      */
15559     getCount : function(){
15560         return this.data.length || 0;
15561     },
15562
15563     /**
15564      * Gets the total number of records in the dataset as returned by the server.
15565      * <p>
15566      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15567      * the dataset size</em>
15568      */
15569     getTotalCount : function(){
15570         return this.totalLength || 0;
15571     },
15572
15573     /**
15574      * Returns the sort state of the Store as an object with two properties:
15575      * <pre><code>
15576  field {String} The name of the field by which the Records are sorted
15577  direction {String} The sort order, "ASC" or "DESC"
15578      * </code></pre>
15579      */
15580     getSortState : function(){
15581         return this.sortInfo;
15582     },
15583
15584     // private
15585     applySort : function(){
15586         if(this.sortInfo && !this.remoteSort){
15587             var s = this.sortInfo, f = s.field;
15588             var st = this.fields.get(f).sortType;
15589             var fn = function(r1, r2){
15590                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15591                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15592             };
15593             this.data.sort(s.direction, fn);
15594             if(this.snapshot && this.snapshot != this.data){
15595                 this.snapshot.sort(s.direction, fn);
15596             }
15597         }
15598     },
15599
15600     /**
15601      * Sets the default sort column and order to be used by the next load operation.
15602      * @param {String} fieldName The name of the field to sort by.
15603      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15604      */
15605     setDefaultSort : function(field, dir){
15606         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15607     },
15608
15609     /**
15610      * Sort the Records.
15611      * If remote sorting is used, the sort is performed on the server, and the cache is
15612      * reloaded. If local sorting is used, the cache is sorted internally.
15613      * @param {String} fieldName The name of the field to sort by.
15614      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15615      */
15616     sort : function(fieldName, dir){
15617         var f = this.fields.get(fieldName);
15618         if(!dir){
15619             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15620             
15621             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15622                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15623             }else{
15624                 dir = f.sortDir;
15625             }
15626         }
15627         this.sortToggle[f.name] = dir;
15628         this.sortInfo = {field: f.name, direction: dir};
15629         if(!this.remoteSort){
15630             this.applySort();
15631             this.fireEvent("datachanged", this);
15632         }else{
15633             this.load(this.lastOptions);
15634         }
15635     },
15636
15637     /**
15638      * Calls the specified function for each of the Records in the cache.
15639      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15640      * Returning <em>false</em> aborts and exits the iteration.
15641      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15642      */
15643     each : function(fn, scope){
15644         this.data.each(fn, scope);
15645     },
15646
15647     /**
15648      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15649      * (e.g., during paging).
15650      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15651      */
15652     getModifiedRecords : function(){
15653         return this.modified;
15654     },
15655
15656     // private
15657     createFilterFn : function(property, value, anyMatch){
15658         if(!value.exec){ // not a regex
15659             value = String(value);
15660             if(value.length == 0){
15661                 return false;
15662             }
15663             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15664         }
15665         return function(r){
15666             return value.test(r.data[property]);
15667         };
15668     },
15669
15670     /**
15671      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15672      * @param {String} property A field on your records
15673      * @param {Number} start The record index to start at (defaults to 0)
15674      * @param {Number} end The last record index to include (defaults to length - 1)
15675      * @return {Number} The sum
15676      */
15677     sum : function(property, start, end){
15678         var rs = this.data.items, v = 0;
15679         start = start || 0;
15680         end = (end || end === 0) ? end : rs.length-1;
15681
15682         for(var i = start; i <= end; i++){
15683             v += (rs[i].data[property] || 0);
15684         }
15685         return v;
15686     },
15687
15688     /**
15689      * Filter the records by a specified property.
15690      * @param {String} field A field on your records
15691      * @param {String/RegExp} value Either a string that the field
15692      * should start with or a RegExp to test against the field
15693      * @param {Boolean} anyMatch True to match any part not just the beginning
15694      */
15695     filter : function(property, value, anyMatch){
15696         var fn = this.createFilterFn(property, value, anyMatch);
15697         return fn ? this.filterBy(fn) : this.clearFilter();
15698     },
15699
15700     /**
15701      * Filter by a function. The specified function will be called with each
15702      * record in this data source. If the function returns true the record is included,
15703      * otherwise it is filtered.
15704      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15705      * @param {Object} scope (optional) The scope of the function (defaults to this)
15706      */
15707     filterBy : function(fn, scope){
15708         this.snapshot = this.snapshot || this.data;
15709         this.data = this.queryBy(fn, scope||this);
15710         this.fireEvent("datachanged", this);
15711     },
15712
15713     /**
15714      * Query the records by a specified property.
15715      * @param {String} field A field on your records
15716      * @param {String/RegExp} value Either a string that the field
15717      * should start with or a RegExp to test against the field
15718      * @param {Boolean} anyMatch True to match any part not just the beginning
15719      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15720      */
15721     query : function(property, value, anyMatch){
15722         var fn = this.createFilterFn(property, value, anyMatch);
15723         return fn ? this.queryBy(fn) : this.data.clone();
15724     },
15725
15726     /**
15727      * Query by a function. The specified function will be called with each
15728      * record in this data source. If the function returns true the record is included
15729      * in the results.
15730      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15731      * @param {Object} scope (optional) The scope of the function (defaults to this)
15732       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15733      **/
15734     queryBy : function(fn, scope){
15735         var data = this.snapshot || this.data;
15736         return data.filterBy(fn, scope||this);
15737     },
15738
15739     /**
15740      * Collects unique values for a particular dataIndex from this store.
15741      * @param {String} dataIndex The property to collect
15742      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15743      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15744      * @return {Array} An array of the unique values
15745      **/
15746     collect : function(dataIndex, allowNull, bypassFilter){
15747         var d = (bypassFilter === true && this.snapshot) ?
15748                 this.snapshot.items : this.data.items;
15749         var v, sv, r = [], l = {};
15750         for(var i = 0, len = d.length; i < len; i++){
15751             v = d[i].data[dataIndex];
15752             sv = String(v);
15753             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15754                 l[sv] = true;
15755                 r[r.length] = v;
15756             }
15757         }
15758         return r;
15759     },
15760
15761     /**
15762      * Revert to a view of the Record cache with no filtering applied.
15763      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15764      */
15765     clearFilter : function(suppressEvent){
15766         if(this.snapshot && this.snapshot != this.data){
15767             this.data = this.snapshot;
15768             delete this.snapshot;
15769             if(suppressEvent !== true){
15770                 this.fireEvent("datachanged", this);
15771             }
15772         }
15773     },
15774
15775     // private
15776     afterEdit : function(record){
15777         if(this.modified.indexOf(record) == -1){
15778             this.modified.push(record);
15779         }
15780         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15781     },
15782     
15783     // private
15784     afterReject : function(record){
15785         this.modified.remove(record);
15786         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15787     },
15788
15789     // private
15790     afterCommit : function(record){
15791         this.modified.remove(record);
15792         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15793     },
15794
15795     /**
15796      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15797      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15798      */
15799     commitChanges : function(){
15800         var m = this.modified.slice(0);
15801         this.modified = [];
15802         for(var i = 0, len = m.length; i < len; i++){
15803             m[i].commit();
15804         }
15805     },
15806
15807     /**
15808      * Cancel outstanding changes on all changed records.
15809      */
15810     rejectChanges : function(){
15811         var m = this.modified.slice(0);
15812         this.modified = [];
15813         for(var i = 0, len = m.length; i < len; i++){
15814             m[i].reject();
15815         }
15816     },
15817
15818     onMetaChange : function(meta, rtype, o){
15819         this.recordType = rtype;
15820         this.fields = rtype.prototype.fields;
15821         delete this.snapshot;
15822         this.sortInfo = meta.sortInfo || this.sortInfo;
15823         this.modified = [];
15824         this.fireEvent('metachange', this, this.reader.meta);
15825     },
15826     
15827     moveIndex : function(data, type)
15828     {
15829         var index = this.indexOf(data);
15830         
15831         var newIndex = index + type;
15832         
15833         this.remove(data);
15834         
15835         this.insert(newIndex, data);
15836         
15837     }
15838 });/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849 /**
15850  * @class Roo.data.SimpleStore
15851  * @extends Roo.data.Store
15852  * Small helper class to make creating Stores from Array data easier.
15853  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15854  * @cfg {Array} fields An array of field definition objects, or field name strings.
15855  * @cfg {Object} an existing reader (eg. copied from another store)
15856  * @cfg {Array} data The multi-dimensional array of data
15857  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15858  * @cfg {Roo.data.Reader} reader  [not-required] 
15859  * @constructor
15860  * @param {Object} config
15861  */
15862 Roo.data.SimpleStore = function(config)
15863 {
15864     Roo.data.SimpleStore.superclass.constructor.call(this, {
15865         isLocal : true,
15866         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15867                 id: config.id
15868             },
15869             Roo.data.Record.create(config.fields)
15870         ),
15871         proxy : new Roo.data.MemoryProxy(config.data)
15872     });
15873     this.load();
15874 };
15875 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15876  * Based on:
15877  * Ext JS Library 1.1.1
15878  * Copyright(c) 2006-2007, Ext JS, LLC.
15879  *
15880  * Originally Released Under LGPL - original licence link has changed is not relivant.
15881  *
15882  * Fork - LGPL
15883  * <script type="text/javascript">
15884  */
15885
15886 /**
15887 /**
15888  * @extends Roo.data.Store
15889  * @class Roo.data.JsonStore
15890  * Small helper class to make creating Stores for JSON data easier. <br/>
15891 <pre><code>
15892 var store = new Roo.data.JsonStore({
15893     url: 'get-images.php',
15894     root: 'images',
15895     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15896 });
15897 </code></pre>
15898  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15899  * JsonReader and HttpProxy (unless inline data is provided).</b>
15900  * @cfg {Array} fields An array of field definition objects, or field name strings.
15901  * @constructor
15902  * @param {Object} config
15903  */
15904 Roo.data.JsonStore = function(c){
15905     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15906         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15907         reader: new Roo.data.JsonReader(c, c.fields)
15908     }));
15909 };
15910 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15911  * Based on:
15912  * Ext JS Library 1.1.1
15913  * Copyright(c) 2006-2007, Ext JS, LLC.
15914  *
15915  * Originally Released Under LGPL - original licence link has changed is not relivant.
15916  *
15917  * Fork - LGPL
15918  * <script type="text/javascript">
15919  */
15920
15921  
15922 Roo.data.Field = function(config){
15923     if(typeof config == "string"){
15924         config = {name: config};
15925     }
15926     Roo.apply(this, config);
15927     
15928     if(!this.type){
15929         this.type = "auto";
15930     }
15931     
15932     var st = Roo.data.SortTypes;
15933     // named sortTypes are supported, here we look them up
15934     if(typeof this.sortType == "string"){
15935         this.sortType = st[this.sortType];
15936     }
15937     
15938     // set default sortType for strings and dates
15939     if(!this.sortType){
15940         switch(this.type){
15941             case "string":
15942                 this.sortType = st.asUCString;
15943                 break;
15944             case "date":
15945                 this.sortType = st.asDate;
15946                 break;
15947             default:
15948                 this.sortType = st.none;
15949         }
15950     }
15951
15952     // define once
15953     var stripRe = /[\$,%]/g;
15954
15955     // prebuilt conversion function for this field, instead of
15956     // switching every time we're reading a value
15957     if(!this.convert){
15958         var cv, dateFormat = this.dateFormat;
15959         switch(this.type){
15960             case "":
15961             case "auto":
15962             case undefined:
15963                 cv = function(v){ return v; };
15964                 break;
15965             case "string":
15966                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15967                 break;
15968             case "int":
15969                 cv = function(v){
15970                     return v !== undefined && v !== null && v !== '' ?
15971                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15972                     };
15973                 break;
15974             case "float":
15975                 cv = function(v){
15976                     return v !== undefined && v !== null && v !== '' ?
15977                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15978                     };
15979                 break;
15980             case "bool":
15981             case "boolean":
15982                 cv = function(v){ return v === true || v === "true" || v == 1; };
15983                 break;
15984             case "date":
15985                 cv = function(v){
15986                     if(!v){
15987                         return '';
15988                     }
15989                     if(v instanceof Date){
15990                         return v;
15991                     }
15992                     if(dateFormat){
15993                         if(dateFormat == "timestamp"){
15994                             return new Date(v*1000);
15995                         }
15996                         return Date.parseDate(v, dateFormat);
15997                     }
15998                     var parsed = Date.parse(v);
15999                     return parsed ? new Date(parsed) : null;
16000                 };
16001              break;
16002             
16003         }
16004         this.convert = cv;
16005     }
16006 };
16007
16008 Roo.data.Field.prototype = {
16009     dateFormat: null,
16010     defaultValue: "",
16011     mapping: null,
16012     sortType : null,
16013     sortDir : "ASC"
16014 };/*
16015  * Based on:
16016  * Ext JS Library 1.1.1
16017  * Copyright(c) 2006-2007, Ext JS, LLC.
16018  *
16019  * Originally Released Under LGPL - original licence link has changed is not relivant.
16020  *
16021  * Fork - LGPL
16022  * <script type="text/javascript">
16023  */
16024  
16025 // Base class for reading structured data from a data source.  This class is intended to be
16026 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16027
16028 /**
16029  * @class Roo.data.DataReader
16030  * @abstract
16031  * Base class for reading structured data from a data source.  This class is intended to be
16032  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16033  */
16034
16035 Roo.data.DataReader = function(meta, recordType){
16036     
16037     this.meta = meta;
16038     
16039     this.recordType = recordType instanceof Array ? 
16040         Roo.data.Record.create(recordType) : recordType;
16041 };
16042
16043 Roo.data.DataReader.prototype = {
16044     
16045     
16046     readerType : 'Data',
16047      /**
16048      * Create an empty record
16049      * @param {Object} data (optional) - overlay some values
16050      * @return {Roo.data.Record} record created.
16051      */
16052     newRow :  function(d) {
16053         var da =  {};
16054         this.recordType.prototype.fields.each(function(c) {
16055             switch( c.type) {
16056                 case 'int' : da[c.name] = 0; break;
16057                 case 'date' : da[c.name] = new Date(); break;
16058                 case 'float' : da[c.name] = 0.0; break;
16059                 case 'boolean' : da[c.name] = false; break;
16060                 default : da[c.name] = ""; break;
16061             }
16062             
16063         });
16064         return new this.recordType(Roo.apply(da, d));
16065     }
16066     
16067     
16068 };/*
16069  * Based on:
16070  * Ext JS Library 1.1.1
16071  * Copyright(c) 2006-2007, Ext JS, LLC.
16072  *
16073  * Originally Released Under LGPL - original licence link has changed is not relivant.
16074  *
16075  * Fork - LGPL
16076  * <script type="text/javascript">
16077  */
16078
16079 /**
16080  * @class Roo.data.DataProxy
16081  * @extends Roo.util.Observable
16082  * @abstract
16083  * This class is an abstract base class for implementations which provide retrieval of
16084  * unformatted data objects.<br>
16085  * <p>
16086  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16087  * (of the appropriate type which knows how to parse the data object) to provide a block of
16088  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16089  * <p>
16090  * Custom implementations must implement the load method as described in
16091  * {@link Roo.data.HttpProxy#load}.
16092  */
16093 Roo.data.DataProxy = function(){
16094     this.addEvents({
16095         /**
16096          * @event beforeload
16097          * Fires before a network request is made to retrieve a data object.
16098          * @param {Object} This DataProxy object.
16099          * @param {Object} params The params parameter to the load function.
16100          */
16101         beforeload : true,
16102         /**
16103          * @event load
16104          * Fires before the load method's callback is called.
16105          * @param {Object} This DataProxy object.
16106          * @param {Object} o The data object.
16107          * @param {Object} arg The callback argument object passed to the load function.
16108          */
16109         load : true,
16110         /**
16111          * @event loadexception
16112          * Fires if an Exception occurs during data retrieval.
16113          * @param {Object} This DataProxy object.
16114          * @param {Object} o The data object.
16115          * @param {Object} arg The callback argument object passed to the load function.
16116          * @param {Object} e The Exception.
16117          */
16118         loadexception : true
16119     });
16120     Roo.data.DataProxy.superclass.constructor.call(this);
16121 };
16122
16123 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16124
16125     /**
16126      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16127      */
16128 /*
16129  * Based on:
16130  * Ext JS Library 1.1.1
16131  * Copyright(c) 2006-2007, Ext JS, LLC.
16132  *
16133  * Originally Released Under LGPL - original licence link has changed is not relivant.
16134  *
16135  * Fork - LGPL
16136  * <script type="text/javascript">
16137  */
16138 /**
16139  * @class Roo.data.MemoryProxy
16140  * @extends Roo.data.DataProxy
16141  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16142  * to the Reader when its load method is called.
16143  * @constructor
16144  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16145  */
16146 Roo.data.MemoryProxy = function(config){
16147     var data = config;
16148     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16149         data = config.data;
16150     }
16151     Roo.data.MemoryProxy.superclass.constructor.call(this);
16152     this.data = data;
16153 };
16154
16155 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16156     
16157     /**
16158      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16159      */
16160     /**
16161      * Load data from the requested source (in this case an in-memory
16162      * data object passed to the constructor), read the data object into
16163      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16164      * process that block using the passed callback.
16165      * @param {Object} params This parameter is not used by the MemoryProxy class.
16166      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16167      * object into a block of Roo.data.Records.
16168      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16169      * The function must be passed <ul>
16170      * <li>The Record block object</li>
16171      * <li>The "arg" argument from the load function</li>
16172      * <li>A boolean success indicator</li>
16173      * </ul>
16174      * @param {Object} scope The scope in which to call the callback
16175      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16176      */
16177     load : function(params, reader, callback, scope, arg){
16178         params = params || {};
16179         var result;
16180         try {
16181             result = reader.readRecords(params.data ? params.data :this.data);
16182         }catch(e){
16183             this.fireEvent("loadexception", this, arg, null, e);
16184             callback.call(scope, null, arg, false);
16185             return;
16186         }
16187         callback.call(scope, result, arg, true);
16188     },
16189     
16190     // private
16191     update : function(params, records){
16192         
16193     }
16194 });/*
16195  * Based on:
16196  * Ext JS Library 1.1.1
16197  * Copyright(c) 2006-2007, Ext JS, LLC.
16198  *
16199  * Originally Released Under LGPL - original licence link has changed is not relivant.
16200  *
16201  * Fork - LGPL
16202  * <script type="text/javascript">
16203  */
16204 /**
16205  * @class Roo.data.HttpProxy
16206  * @extends Roo.data.DataProxy
16207  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16208  * configured to reference a certain URL.<br><br>
16209  * <p>
16210  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16211  * from which the running page was served.<br><br>
16212  * <p>
16213  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16214  * <p>
16215  * Be aware that to enable the browser to parse an XML document, the server must set
16216  * the Content-Type header in the HTTP response to "text/xml".
16217  * @constructor
16218  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16219  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16220  * will be used to make the request.
16221  */
16222 Roo.data.HttpProxy = function(conn){
16223     Roo.data.HttpProxy.superclass.constructor.call(this);
16224     // is conn a conn config or a real conn?
16225     this.conn = conn;
16226     this.useAjax = !conn || !conn.events;
16227   
16228 };
16229
16230 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16231     // thse are take from connection...
16232     
16233     /**
16234      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16235      */
16236     /**
16237      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16238      * extra parameters to each request made by this object. (defaults to undefined)
16239      */
16240     /**
16241      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16242      *  to each request made by this object. (defaults to undefined)
16243      */
16244     /**
16245      * @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)
16246      */
16247     /**
16248      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16249      */
16250      /**
16251      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16252      * @type Boolean
16253      */
16254   
16255
16256     /**
16257      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16258      * @type Boolean
16259      */
16260     /**
16261      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16262      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16263      * a finer-grained basis than the DataProxy events.
16264      */
16265     getConnection : function(){
16266         return this.useAjax ? Roo.Ajax : this.conn;
16267     },
16268
16269     /**
16270      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16271      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16272      * process that block using the passed callback.
16273      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16274      * for the request to the remote server.
16275      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16276      * object into a block of Roo.data.Records.
16277      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16278      * The function must be passed <ul>
16279      * <li>The Record block object</li>
16280      * <li>The "arg" argument from the load function</li>
16281      * <li>A boolean success indicator</li>
16282      * </ul>
16283      * @param {Object} scope The scope in which to call the callback
16284      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16285      */
16286     load : function(params, reader, callback, scope, arg){
16287         if(this.fireEvent("beforeload", this, params) !== false){
16288             var  o = {
16289                 params : params || {},
16290                 request: {
16291                     callback : callback,
16292                     scope : scope,
16293                     arg : arg
16294                 },
16295                 reader: reader,
16296                 callback : this.loadResponse,
16297                 scope: this
16298             };
16299             if(this.useAjax){
16300                 Roo.applyIf(o, this.conn);
16301                 if(this.activeRequest){
16302                     Roo.Ajax.abort(this.activeRequest);
16303                 }
16304                 this.activeRequest = Roo.Ajax.request(o);
16305             }else{
16306                 this.conn.request(o);
16307             }
16308         }else{
16309             callback.call(scope||this, null, arg, false);
16310         }
16311     },
16312
16313     // private
16314     loadResponse : function(o, success, response){
16315         delete this.activeRequest;
16316         if(!success){
16317             this.fireEvent("loadexception", this, o, response);
16318             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16319             return;
16320         }
16321         var result;
16322         try {
16323             result = o.reader.read(response);
16324         }catch(e){
16325             o.success = false;
16326             o.raw = { errorMsg : response.responseText };
16327             this.fireEvent("loadexception", this, o, response, e);
16328             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16329             return;
16330         }
16331         
16332         this.fireEvent("load", this, o, o.request.arg);
16333         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16334     },
16335
16336     // private
16337     update : function(dataSet){
16338
16339     },
16340
16341     // private
16342     updateResponse : function(dataSet){
16343
16344     }
16345 });/*
16346  * Based on:
16347  * Ext JS Library 1.1.1
16348  * Copyright(c) 2006-2007, Ext JS, LLC.
16349  *
16350  * Originally Released Under LGPL - original licence link has changed is not relivant.
16351  *
16352  * Fork - LGPL
16353  * <script type="text/javascript">
16354  */
16355
16356 /**
16357  * @class Roo.data.ScriptTagProxy
16358  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16359  * other than the originating domain of the running page.<br><br>
16360  * <p>
16361  * <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
16362  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16363  * <p>
16364  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16365  * source code that is used as the source inside a &lt;script> tag.<br><br>
16366  * <p>
16367  * In order for the browser to process the returned data, the server must wrap the data object
16368  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16369  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16370  * depending on whether the callback name was passed:
16371  * <p>
16372  * <pre><code>
16373 boolean scriptTag = false;
16374 String cb = request.getParameter("callback");
16375 if (cb != null) {
16376     scriptTag = true;
16377     response.setContentType("text/javascript");
16378 } else {
16379     response.setContentType("application/x-json");
16380 }
16381 Writer out = response.getWriter();
16382 if (scriptTag) {
16383     out.write(cb + "(");
16384 }
16385 out.print(dataBlock.toJsonString());
16386 if (scriptTag) {
16387     out.write(");");
16388 }
16389 </pre></code>
16390  *
16391  * @constructor
16392  * @param {Object} config A configuration object.
16393  */
16394 Roo.data.ScriptTagProxy = function(config){
16395     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16396     Roo.apply(this, config);
16397     this.head = document.getElementsByTagName("head")[0];
16398 };
16399
16400 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16401
16402 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16403     /**
16404      * @cfg {String} url The URL from which to request the data object.
16405      */
16406     /**
16407      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16408      */
16409     timeout : 30000,
16410     /**
16411      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16412      * the server the name of the callback function set up by the load call to process the returned data object.
16413      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16414      * javascript output which calls this named function passing the data object as its only parameter.
16415      */
16416     callbackParam : "callback",
16417     /**
16418      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16419      * name to the request.
16420      */
16421     nocache : true,
16422
16423     /**
16424      * Load data from the configured URL, read the data object into
16425      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16426      * process that block using the passed callback.
16427      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16428      * for the request to the remote server.
16429      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16430      * object into a block of Roo.data.Records.
16431      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16432      * The function must be passed <ul>
16433      * <li>The Record block object</li>
16434      * <li>The "arg" argument from the load function</li>
16435      * <li>A boolean success indicator</li>
16436      * </ul>
16437      * @param {Object} scope The scope in which to call the callback
16438      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16439      */
16440     load : function(params, reader, callback, scope, arg){
16441         if(this.fireEvent("beforeload", this, params) !== false){
16442
16443             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16444
16445             var url = this.url;
16446             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16447             if(this.nocache){
16448                 url += "&_dc=" + (new Date().getTime());
16449             }
16450             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16451             var trans = {
16452                 id : transId,
16453                 cb : "stcCallback"+transId,
16454                 scriptId : "stcScript"+transId,
16455                 params : params,
16456                 arg : arg,
16457                 url : url,
16458                 callback : callback,
16459                 scope : scope,
16460                 reader : reader
16461             };
16462             var conn = this;
16463
16464             window[trans.cb] = function(o){
16465                 conn.handleResponse(o, trans);
16466             };
16467
16468             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16469
16470             if(this.autoAbort !== false){
16471                 this.abort();
16472             }
16473
16474             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16475
16476             var script = document.createElement("script");
16477             script.setAttribute("src", url);
16478             script.setAttribute("type", "text/javascript");
16479             script.setAttribute("id", trans.scriptId);
16480             this.head.appendChild(script);
16481
16482             this.trans = trans;
16483         }else{
16484             callback.call(scope||this, null, arg, false);
16485         }
16486     },
16487
16488     // private
16489     isLoading : function(){
16490         return this.trans ? true : false;
16491     },
16492
16493     /**
16494      * Abort the current server request.
16495      */
16496     abort : function(){
16497         if(this.isLoading()){
16498             this.destroyTrans(this.trans);
16499         }
16500     },
16501
16502     // private
16503     destroyTrans : function(trans, isLoaded){
16504         this.head.removeChild(document.getElementById(trans.scriptId));
16505         clearTimeout(trans.timeoutId);
16506         if(isLoaded){
16507             window[trans.cb] = undefined;
16508             try{
16509                 delete window[trans.cb];
16510             }catch(e){}
16511         }else{
16512             // if hasn't been loaded, wait for load to remove it to prevent script error
16513             window[trans.cb] = function(){
16514                 window[trans.cb] = undefined;
16515                 try{
16516                     delete window[trans.cb];
16517                 }catch(e){}
16518             };
16519         }
16520     },
16521
16522     // private
16523     handleResponse : function(o, trans){
16524         this.trans = false;
16525         this.destroyTrans(trans, true);
16526         var result;
16527         try {
16528             result = trans.reader.readRecords(o);
16529         }catch(e){
16530             this.fireEvent("loadexception", this, o, trans.arg, e);
16531             trans.callback.call(trans.scope||window, null, trans.arg, false);
16532             return;
16533         }
16534         this.fireEvent("load", this, o, trans.arg);
16535         trans.callback.call(trans.scope||window, result, trans.arg, true);
16536     },
16537
16538     // private
16539     handleFailure : function(trans){
16540         this.trans = false;
16541         this.destroyTrans(trans, false);
16542         this.fireEvent("loadexception", this, null, trans.arg);
16543         trans.callback.call(trans.scope||window, null, trans.arg, false);
16544     }
16545 });/*
16546  * Based on:
16547  * Ext JS Library 1.1.1
16548  * Copyright(c) 2006-2007, Ext JS, LLC.
16549  *
16550  * Originally Released Under LGPL - original licence link has changed is not relivant.
16551  *
16552  * Fork - LGPL
16553  * <script type="text/javascript">
16554  */
16555
16556 /**
16557  * @class Roo.data.JsonReader
16558  * @extends Roo.data.DataReader
16559  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16560  * based on mappings in a provided Roo.data.Record constructor.
16561  * 
16562  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16563  * in the reply previously. 
16564  * 
16565  * <p>
16566  * Example code:
16567  * <pre><code>
16568 var RecordDef = Roo.data.Record.create([
16569     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16570     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16571 ]);
16572 var myReader = new Roo.data.JsonReader({
16573     totalProperty: "results",    // The property which contains the total dataset size (optional)
16574     root: "rows",                // The property which contains an Array of row objects
16575     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16576 }, RecordDef);
16577 </code></pre>
16578  * <p>
16579  * This would consume a JSON file like this:
16580  * <pre><code>
16581 { 'results': 2, 'rows': [
16582     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16583     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16584 }
16585 </code></pre>
16586  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16587  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16588  * paged from the remote server.
16589  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16590  * @cfg {String} root name of the property which contains the Array of row objects.
16591  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16592  * @cfg {Array} fields Array of field definition objects
16593  * @constructor
16594  * Create a new JsonReader
16595  * @param {Object} meta Metadata configuration options
16596  * @param {Object} recordType Either an Array of field definition objects,
16597  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16598  */
16599 Roo.data.JsonReader = function(meta, recordType){
16600     
16601     meta = meta || {};
16602     // set some defaults:
16603     Roo.applyIf(meta, {
16604         totalProperty: 'total',
16605         successProperty : 'success',
16606         root : 'data',
16607         id : 'id'
16608     });
16609     
16610     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16611 };
16612 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16613     
16614     readerType : 'Json',
16615     
16616     /**
16617      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16618      * Used by Store query builder to append _requestMeta to params.
16619      * 
16620      */
16621     metaFromRemote : false,
16622     /**
16623      * This method is only used by a DataProxy which has retrieved data from a remote server.
16624      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16625      * @return {Object} data A data block which is used by an Roo.data.Store object as
16626      * a cache of Roo.data.Records.
16627      */
16628     read : function(response){
16629         var json = response.responseText;
16630        
16631         var o = /* eval:var:o */ eval("("+json+")");
16632         if(!o) {
16633             throw {message: "JsonReader.read: Json object not found"};
16634         }
16635         
16636         if(o.metaData){
16637             
16638             delete this.ef;
16639             this.metaFromRemote = true;
16640             this.meta = o.metaData;
16641             this.recordType = Roo.data.Record.create(o.metaData.fields);
16642             this.onMetaChange(this.meta, this.recordType, o);
16643         }
16644         return this.readRecords(o);
16645     },
16646
16647     // private function a store will implement
16648     onMetaChange : function(meta, recordType, o){
16649
16650     },
16651
16652     /**
16653          * @ignore
16654          */
16655     simpleAccess: function(obj, subsc) {
16656         return obj[subsc];
16657     },
16658
16659         /**
16660          * @ignore
16661          */
16662     getJsonAccessor: function(){
16663         var re = /[\[\.]/;
16664         return function(expr) {
16665             try {
16666                 return(re.test(expr))
16667                     ? new Function("obj", "return obj." + expr)
16668                     : function(obj){
16669                         return obj[expr];
16670                     };
16671             } catch(e){}
16672             return Roo.emptyFn;
16673         };
16674     }(),
16675
16676     /**
16677      * Create a data block containing Roo.data.Records from an XML document.
16678      * @param {Object} o An object which contains an Array of row objects in the property specified
16679      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16680      * which contains the total size of the dataset.
16681      * @return {Object} data A data block which is used by an Roo.data.Store object as
16682      * a cache of Roo.data.Records.
16683      */
16684     readRecords : function(o){
16685         /**
16686          * After any data loads, the raw JSON data is available for further custom processing.
16687          * @type Object
16688          */
16689         this.o = o;
16690         var s = this.meta, Record = this.recordType,
16691             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16692
16693 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16694         if (!this.ef) {
16695             if(s.totalProperty) {
16696                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16697                 }
16698                 if(s.successProperty) {
16699                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16700                 }
16701                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16702                 if (s.id) {
16703                         var g = this.getJsonAccessor(s.id);
16704                         this.getId = function(rec) {
16705                                 var r = g(rec);  
16706                                 return (r === undefined || r === "") ? null : r;
16707                         };
16708                 } else {
16709                         this.getId = function(){return null;};
16710                 }
16711             this.ef = [];
16712             for(var jj = 0; jj < fl; jj++){
16713                 f = fi[jj];
16714                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16715                 this.ef[jj] = this.getJsonAccessor(map);
16716             }
16717         }
16718
16719         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16720         if(s.totalProperty){
16721             var vt = parseInt(this.getTotal(o), 10);
16722             if(!isNaN(vt)){
16723                 totalRecords = vt;
16724             }
16725         }
16726         if(s.successProperty){
16727             var vs = this.getSuccess(o);
16728             if(vs === false || vs === 'false'){
16729                 success = false;
16730             }
16731         }
16732         var records = [];
16733         for(var i = 0; i < c; i++){
16734             var n = root[i];
16735             var values = {};
16736             var id = this.getId(n);
16737             for(var j = 0; j < fl; j++){
16738                 f = fi[j];
16739                                 var v = this.ef[j](n);
16740                                 if (!f.convert) {
16741                                         Roo.log('missing convert for ' + f.name);
16742                                         Roo.log(f);
16743                                         continue;
16744                                 }
16745                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16746             }
16747                         if (!Record) {
16748                                 return {
16749                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16750                                         success : false,
16751                                         records : [],
16752                                         totalRecords : 0
16753                                 };
16754                         }
16755             var record = new Record(values, id);
16756             record.json = n;
16757             records[i] = record;
16758         }
16759         return {
16760             raw : o,
16761             success : success,
16762             records : records,
16763             totalRecords : totalRecords
16764         };
16765     },
16766     // used when loading children.. @see loadDataFromChildren
16767     toLoadData: function(rec)
16768     {
16769         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16770         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16771         return { data : data, total : data.length };
16772         
16773     }
16774 });/*
16775  * Based on:
16776  * Ext JS Library 1.1.1
16777  * Copyright(c) 2006-2007, Ext JS, LLC.
16778  *
16779  * Originally Released Under LGPL - original licence link has changed is not relivant.
16780  *
16781  * Fork - LGPL
16782  * <script type="text/javascript">
16783  */
16784
16785 /**
16786  * @class Roo.data.ArrayReader
16787  * @extends Roo.data.DataReader
16788  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16789  * Each element of that Array represents a row of data fields. The
16790  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16791  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16792  * <p>
16793  * Example code:.
16794  * <pre><code>
16795 var RecordDef = Roo.data.Record.create([
16796     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16797     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16798 ]);
16799 var myReader = new Roo.data.ArrayReader({
16800     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16801 }, RecordDef);
16802 </code></pre>
16803  * <p>
16804  * This would consume an Array like this:
16805  * <pre><code>
16806 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16807   </code></pre>
16808  
16809  * @constructor
16810  * Create a new JsonReader
16811  * @param {Object} meta Metadata configuration options.
16812  * @param {Object|Array} recordType Either an Array of field definition objects
16813  * 
16814  * @cfg {Array} fields Array of field definition objects
16815  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16816  * as specified to {@link Roo.data.Record#create},
16817  * or an {@link Roo.data.Record} object
16818  *
16819  * 
16820  * created using {@link Roo.data.Record#create}.
16821  */
16822 Roo.data.ArrayReader = function(meta, recordType)
16823 {    
16824     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16825 };
16826
16827 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16828     
16829       /**
16830      * Create a data block containing Roo.data.Records from an XML document.
16831      * @param {Object} o An Array of row objects which represents the dataset.
16832      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16833      * a cache of Roo.data.Records.
16834      */
16835     readRecords : function(o)
16836     {
16837         var sid = this.meta ? this.meta.id : null;
16838         var recordType = this.recordType, fields = recordType.prototype.fields;
16839         var records = [];
16840         var root = o;
16841         for(var i = 0; i < root.length; i++){
16842             var n = root[i];
16843             var values = {};
16844             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16845             for(var j = 0, jlen = fields.length; j < jlen; j++){
16846                 var f = fields.items[j];
16847                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16848                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16849                 v = f.convert(v);
16850                 values[f.name] = v;
16851             }
16852             var record = new recordType(values, id);
16853             record.json = n;
16854             records[records.length] = record;
16855         }
16856         return {
16857             records : records,
16858             totalRecords : records.length
16859         };
16860     },
16861     // used when loading children.. @see loadDataFromChildren
16862     toLoadData: function(rec)
16863     {
16864         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16865         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16866         
16867     }
16868     
16869     
16870 });/*
16871  * - LGPL
16872  * * 
16873  */
16874
16875 /**
16876  * @class Roo.bootstrap.form.ComboBox
16877  * @extends Roo.bootstrap.form.TriggerField
16878  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16879  * @cfg {Boolean} append (true|false) default false
16880  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16881  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16882  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16883  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16884  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16885  * @cfg {Boolean} animate default true
16886  * @cfg {Boolean} emptyResultText only for touch device
16887  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16888  * @cfg {String} emptyTitle default ''
16889  * @cfg {Number} width fixed with? experimental
16890  * @constructor
16891  * Create a new ComboBox.
16892  * @param {Object} config Configuration options
16893  */
16894 Roo.bootstrap.form.ComboBox = function(config){
16895     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16896     this.addEvents({
16897         /**
16898          * @event expand
16899          * Fires when the dropdown list is expanded
16900         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901         */
16902         'expand' : true,
16903         /**
16904          * @event collapse
16905          * Fires when the dropdown list is collapsed
16906         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16907         */
16908         'collapse' : true,
16909         /**
16910          * @event beforeselect
16911          * Fires before a list item is selected. Return false to cancel the selection.
16912         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913         * @param {Roo.data.Record} record The data record returned from the underlying store
16914         * @param {Number} index The index of the selected item in the dropdown list
16915         */
16916         'beforeselect' : true,
16917         /**
16918          * @event select
16919          * Fires when a list item is selected
16920         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16922         * @param {Number} index The index of the selected item in the dropdown list
16923         */
16924         'select' : true,
16925         /**
16926          * @event beforequery
16927          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16928          * The event object passed has these properties:
16929         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930         * @param {String} query The query
16931         * @param {Boolean} forceAll true to force "all" query
16932         * @param {Boolean} cancel true to cancel the query
16933         * @param {Object} e The query event object
16934         */
16935         'beforequery': true,
16936          /**
16937          * @event add
16938          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16939         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16940         */
16941         'add' : true,
16942         /**
16943          * @event edit
16944          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16945         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16946         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16947         */
16948         'edit' : true,
16949         /**
16950          * @event remove
16951          * Fires when the remove value from the combobox array
16952         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16953         */
16954         'remove' : true,
16955         /**
16956          * @event afterremove
16957          * Fires when the remove value from the combobox array
16958         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16959         */
16960         'afterremove' : true,
16961         /**
16962          * @event specialfilter
16963          * Fires when specialfilter
16964             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16965             */
16966         'specialfilter' : true,
16967         /**
16968          * @event tick
16969          * Fires when tick the element
16970             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16971             */
16972         'tick' : true,
16973         /**
16974          * @event touchviewdisplay
16975          * Fires when touch view require special display (default is using displayField)
16976             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16977             * @param {Object} cfg set html .
16978             */
16979         'touchviewdisplay' : true
16980         
16981     });
16982     
16983     this.item = [];
16984     this.tickItems = [];
16985     
16986     this.selectedIndex = -1;
16987     if(this.mode == 'local'){
16988         if(config.queryDelay === undefined){
16989             this.queryDelay = 10;
16990         }
16991         if(config.minChars === undefined){
16992             this.minChars = 0;
16993         }
16994     }
16995 };
16996
16997 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16998      
16999     /**
17000      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
17001      * rendering into an Roo.Editor, defaults to false)
17002      */
17003     /**
17004      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17005      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17006      */
17007     /**
17008      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17009      */
17010     /**
17011      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17012      * the dropdown list (defaults to undefined, with no header element)
17013      */
17014
17015      /**
17016      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17017      */
17018      
17019      /**
17020      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17021      */
17022     listWidth: undefined,
17023     /**
17024      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17025      * mode = 'remote' or 'text' if mode = 'local')
17026      */
17027     displayField: undefined,
17028     
17029     /**
17030      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17031      * mode = 'remote' or 'value' if mode = 'local'). 
17032      * Note: use of a valueField requires the user make a selection
17033      * in order for a value to be mapped.
17034      */
17035     valueField: undefined,
17036     /**
17037      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17038      */
17039     modalTitle : '',
17040     
17041     /**
17042      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17043      * field's data value (defaults to the underlying DOM element's name)
17044      */
17045     hiddenName: undefined,
17046     /**
17047      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17048      */
17049     listClass: '',
17050     /**
17051      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17052      */
17053     selectedClass: 'active',
17054     
17055     /**
17056      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17057      */
17058     shadow:'sides',
17059     /**
17060      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17061      * anchor positions (defaults to 'tl-bl')
17062      */
17063     listAlign: 'tl-bl?',
17064     /**
17065      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17066      */
17067     maxHeight: 300,
17068     /**
17069      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17070      * query specified by the allQuery config option (defaults to 'query')
17071      */
17072     triggerAction: 'query',
17073     /**
17074      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17075      * (defaults to 4, does not apply if editable = false)
17076      */
17077     minChars : 4,
17078     /**
17079      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17080      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17081      */
17082     typeAhead: false,
17083     /**
17084      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17085      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17086      */
17087     queryDelay: 500,
17088     /**
17089      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17090      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17091      */
17092     pageSize: 0,
17093     /**
17094      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17095      * when editable = true (defaults to false)
17096      */
17097     selectOnFocus:false,
17098     /**
17099      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17100      */
17101     queryParam: 'query',
17102     /**
17103      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17104      * when mode = 'remote' (defaults to 'Loading...')
17105      */
17106     loadingText: 'Loading...',
17107     /**
17108      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17109      */
17110     resizable: false,
17111     /**
17112      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17113      */
17114     handleHeight : 8,
17115     /**
17116      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17117      * traditional select (defaults to true)
17118      */
17119     editable: true,
17120     /**
17121      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17122      */
17123     allQuery: '',
17124     /**
17125      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17126      */
17127     mode: 'remote',
17128     /**
17129      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17130      * listWidth has a higher value)
17131      */
17132     minListWidth : 70,
17133     /**
17134      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17135      * allow the user to set arbitrary text into the field (defaults to false)
17136      */
17137     forceSelection:false,
17138     /**
17139      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17140      * if typeAhead = true (defaults to 250)
17141      */
17142     typeAheadDelay : 250,
17143     /**
17144      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17145      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17146      */
17147     valueNotFoundText : undefined,
17148     /**
17149      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17150      */
17151     blockFocus : false,
17152     
17153     /**
17154      * @cfg {Boolean} disableClear Disable showing of clear button.
17155      */
17156     disableClear : false,
17157     /**
17158      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17159      */
17160     alwaysQuery : false,
17161     
17162     /**
17163      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17164      */
17165     multiple : false,
17166     
17167     /**
17168      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17169      */
17170     invalidClass : "has-warning",
17171     
17172     /**
17173      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17174      */
17175     validClass : "has-success",
17176     
17177     /**
17178      * @cfg {Boolean} specialFilter (true|false) special filter default false
17179      */
17180     specialFilter : false,
17181     
17182     /**
17183      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17184      */
17185     mobileTouchView : true,
17186     
17187     /**
17188      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17189      */
17190     useNativeIOS : false,
17191     
17192     /**
17193      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17194      */
17195     mobile_restrict_height : false,
17196     
17197     ios_options : false,
17198     
17199     //private
17200     addicon : false,
17201     editicon: false,
17202     
17203     page: 0,
17204     hasQuery: false,
17205     append: false,
17206     loadNext: false,
17207     autoFocus : true,
17208     tickable : false,
17209     btnPosition : 'right',
17210     triggerList : true,
17211     showToggleBtn : true,
17212     animate : true,
17213     emptyResultText: 'Empty',
17214     triggerText : 'Select',
17215     emptyTitle : '',
17216     width : false,
17217     
17218     // element that contains real text value.. (when hidden is used..)
17219     
17220     getAutoCreate : function()
17221     {   
17222         var cfg = false;
17223         //render
17224         /*
17225          * Render classic select for iso
17226          */
17227         
17228         if(Roo.isIOS && this.useNativeIOS){
17229             cfg = this.getAutoCreateNativeIOS();
17230             return cfg;
17231         }
17232         
17233         /*
17234          * Touch Devices
17235          */
17236         
17237         if(Roo.isTouch && this.mobileTouchView){
17238             cfg = this.getAutoCreateTouchView();
17239             return cfg;;
17240         }
17241         
17242         /*
17243          *  Normal ComboBox
17244          */
17245         if(!this.tickable){
17246             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17247             return cfg;
17248         }
17249         
17250         /*
17251          *  ComboBox with tickable selections
17252          */
17253              
17254         var align = this.labelAlign || this.parentLabelAlign();
17255         
17256         cfg = {
17257             cls : 'form-group roo-combobox-tickable' //input-group
17258         };
17259         
17260         var btn_text_select = '';
17261         var btn_text_done = '';
17262         var btn_text_cancel = '';
17263         
17264         if (this.btn_text_show) {
17265             btn_text_select = 'Select';
17266             btn_text_done = 'Done';
17267             btn_text_cancel = 'Cancel'; 
17268         }
17269         
17270         var buttons = {
17271             tag : 'div',
17272             cls : 'tickable-buttons',
17273             cn : [
17274                 {
17275                     tag : 'button',
17276                     type : 'button',
17277                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17278                     //html : this.triggerText
17279                     html: btn_text_select
17280                 },
17281                 {
17282                     tag : 'button',
17283                     type : 'button',
17284                     name : 'ok',
17285                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17286                     //html : 'Done'
17287                     html: btn_text_done
17288                 },
17289                 {
17290                     tag : 'button',
17291                     type : 'button',
17292                     name : 'cancel',
17293                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17294                     //html : 'Cancel'
17295                     html: btn_text_cancel
17296                 }
17297             ]
17298         };
17299         
17300         if(this.editable){
17301             buttons.cn.unshift({
17302                 tag: 'input',
17303                 cls: 'roo-select2-search-field-input'
17304             });
17305         }
17306         
17307         var _this = this;
17308         
17309         Roo.each(buttons.cn, function(c){
17310             if (_this.size) {
17311                 c.cls += ' btn-' + _this.size;
17312             }
17313
17314             if (_this.disabled) {
17315                 c.disabled = true;
17316             }
17317         });
17318         
17319         var box = {
17320             tag: 'div',
17321             style : 'display: contents',
17322             cn: [
17323                 {
17324                     tag: 'input',
17325                     type : 'hidden',
17326                     cls: 'form-hidden-field'
17327                 },
17328                 {
17329                     tag: 'ul',
17330                     cls: 'roo-select2-choices',
17331                     cn:[
17332                         {
17333                             tag: 'li',
17334                             cls: 'roo-select2-search-field',
17335                             cn: [
17336                                 buttons
17337                             ]
17338                         }
17339                     ]
17340                 }
17341             ]
17342         };
17343         
17344         var combobox = {
17345             cls: 'roo-select2-container input-group roo-select2-container-multi',
17346             cn: [
17347                 
17348                 box
17349 //                {
17350 //                    tag: 'ul',
17351 //                    cls: 'typeahead typeahead-long dropdown-menu',
17352 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17353 //                }
17354             ]
17355         };
17356         
17357         if(this.hasFeedback && !this.allowBlank){
17358             
17359             var feedback = {
17360                 tag: 'span',
17361                 cls: 'glyphicon form-control-feedback'
17362             };
17363
17364             combobox.cn.push(feedback);
17365         }
17366         
17367         
17368         
17369         var indicator = {
17370             tag : 'i',
17371             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17372             tooltip : 'This field is required'
17373         };
17374         if (Roo.bootstrap.version == 4) {
17375             indicator = {
17376                 tag : 'i',
17377                 style : 'display:none'
17378             };
17379         }
17380         if (align ==='left' && this.fieldLabel.length) {
17381             
17382             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17383             
17384             cfg.cn = [
17385                 indicator,
17386                 {
17387                     tag: 'label',
17388                     'for' :  id,
17389                     cls : 'control-label col-form-label',
17390                     html : this.fieldLabel
17391
17392                 },
17393                 {
17394                     cls : "", 
17395                     cn: [
17396                         combobox
17397                     ]
17398                 }
17399
17400             ];
17401             
17402             var labelCfg = cfg.cn[1];
17403             var contentCfg = cfg.cn[2];
17404             
17405
17406             if(this.indicatorpos == 'right'){
17407                 
17408                 cfg.cn = [
17409                     {
17410                         tag: 'label',
17411                         'for' :  id,
17412                         cls : 'control-label col-form-label',
17413                         cn : [
17414                             {
17415                                 tag : 'span',
17416                                 html : this.fieldLabel
17417                             },
17418                             indicator
17419                         ]
17420                     },
17421                     {
17422                         cls : "",
17423                         cn: [
17424                             combobox
17425                         ]
17426                     }
17427
17428                 ];
17429                 
17430                 
17431                 
17432                 labelCfg = cfg.cn[0];
17433                 contentCfg = cfg.cn[1];
17434             
17435             }
17436             
17437             if(this.labelWidth > 12){
17438                 labelCfg.style = "width: " + this.labelWidth + 'px';
17439             }
17440             if(this.width * 1 > 0){
17441                 contentCfg.style = "width: " + this.width + 'px';
17442             }
17443             if(this.labelWidth < 13 && this.labelmd == 0){
17444                 this.labelmd = this.labelWidth;
17445             }
17446             
17447             if(this.labellg > 0){
17448                 labelCfg.cls += ' col-lg-' + this.labellg;
17449                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17450             }
17451             
17452             if(this.labelmd > 0){
17453                 labelCfg.cls += ' col-md-' + this.labelmd;
17454                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17455             }
17456             
17457             if(this.labelsm > 0){
17458                 labelCfg.cls += ' col-sm-' + this.labelsm;
17459                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17460             }
17461             
17462             if(this.labelxs > 0){
17463                 labelCfg.cls += ' col-xs-' + this.labelxs;
17464                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17465             }
17466                 
17467                 
17468         } else if ( this.fieldLabel.length) {
17469 //                Roo.log(" label");
17470                  cfg.cn = [
17471                    indicator,
17472                     {
17473                         tag: 'label',
17474                         //cls : 'input-group-addon',
17475                         html : this.fieldLabel
17476                     },
17477                     combobox
17478                 ];
17479                 
17480                 if(this.indicatorpos == 'right'){
17481                     cfg.cn = [
17482                         {
17483                             tag: 'label',
17484                             //cls : 'input-group-addon',
17485                             html : this.fieldLabel
17486                         },
17487                         indicator,
17488                         combobox
17489                     ];
17490                     
17491                 }
17492
17493         } else {
17494             
17495 //                Roo.log(" no label && no align");
17496                 cfg = combobox
17497                      
17498                 
17499         }
17500          
17501         var settings=this;
17502         ['xs','sm','md','lg'].map(function(size){
17503             if (settings[size]) {
17504                 cfg.cls += ' col-' + size + '-' + settings[size];
17505             }
17506         });
17507         
17508         return cfg;
17509         
17510     },
17511     
17512     _initEventsCalled : false,
17513     
17514     // private
17515     initEvents: function()
17516     {   
17517         if (this._initEventsCalled) { // as we call render... prevent looping...
17518             return;
17519         }
17520         this._initEventsCalled = true;
17521         
17522         if (!this.store) {
17523             throw "can not find store for combo";
17524         }
17525         
17526         this.indicator = this.indicatorEl();
17527         
17528         this.store = Roo.factory(this.store, Roo.data);
17529         this.store.parent = this;
17530         
17531         // if we are building from html. then this element is so complex, that we can not really
17532         // use the rendered HTML.
17533         // so we have to trash and replace the previous code.
17534         if (Roo.XComponent.build_from_html) {
17535             // remove this element....
17536             var e = this.el.dom, k=0;
17537             while (e ) { e = e.previousSibling;  ++k;}
17538
17539             this.el.remove();
17540             
17541             this.el=false;
17542             this.rendered = false;
17543             
17544             this.render(this.parent().getChildContainer(true), k);
17545         }
17546         
17547         if(Roo.isIOS && this.useNativeIOS){
17548             this.initIOSView();
17549             return;
17550         }
17551         
17552         /*
17553          * Touch Devices
17554          */
17555         
17556         if(Roo.isTouch && this.mobileTouchView){
17557             this.initTouchView();
17558             return;
17559         }
17560         
17561         if(this.tickable){
17562             this.initTickableEvents();
17563             return;
17564         }
17565         
17566         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17567         
17568         if(this.hiddenName){
17569             
17570             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17571             
17572             this.hiddenField.dom.value =
17573                 this.hiddenValue !== undefined ? this.hiddenValue :
17574                 this.value !== undefined ? this.value : '';
17575
17576             // prevent input submission
17577             this.el.dom.removeAttribute('name');
17578             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17579              
17580              
17581         }
17582         //if(Roo.isGecko){
17583         //    this.el.dom.setAttribute('autocomplete', 'off');
17584         //}
17585         
17586         var cls = 'x-combo-list';
17587         
17588         //this.list = new Roo.Layer({
17589         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17590         //});
17591         
17592         var _this = this;
17593         
17594         (function(){
17595             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17596             _this.list.setWidth(lw);
17597         }).defer(100);
17598         
17599         this.list.on('mouseover', this.onViewOver, this);
17600         this.list.on('mousemove', this.onViewMove, this);
17601         this.list.on('scroll', this.onViewScroll, this);
17602         
17603         /*
17604         this.list.swallowEvent('mousewheel');
17605         this.assetHeight = 0;
17606
17607         if(this.title){
17608             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17609             this.assetHeight += this.header.getHeight();
17610         }
17611
17612         this.innerList = this.list.createChild({cls:cls+'-inner'});
17613         this.innerList.on('mouseover', this.onViewOver, this);
17614         this.innerList.on('mousemove', this.onViewMove, this);
17615         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17616         
17617         if(this.allowBlank && !this.pageSize && !this.disableClear){
17618             this.footer = this.list.createChild({cls:cls+'-ft'});
17619             this.pageTb = new Roo.Toolbar(this.footer);
17620            
17621         }
17622         if(this.pageSize){
17623             this.footer = this.list.createChild({cls:cls+'-ft'});
17624             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17625                     {pageSize: this.pageSize});
17626             
17627         }
17628         
17629         if (this.pageTb && this.allowBlank && !this.disableClear) {
17630             var _this = this;
17631             this.pageTb.add(new Roo.Toolbar.Fill(), {
17632                 cls: 'x-btn-icon x-btn-clear',
17633                 text: '&#160;',
17634                 handler: function()
17635                 {
17636                     _this.collapse();
17637                     _this.clearValue();
17638                     _this.onSelect(false, -1);
17639                 }
17640             });
17641         }
17642         if (this.footer) {
17643             this.assetHeight += this.footer.getHeight();
17644         }
17645         */
17646             
17647         if(!this.tpl){
17648             this.tpl = Roo.bootstrap.version == 4 ?
17649                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17650                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17651         }
17652
17653         this.view = new Roo.View(this.list, this.tpl, {
17654             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17655         });
17656         //this.view.wrapEl.setDisplayed(false);
17657         this.view.on('click', this.onViewClick, this);
17658         
17659         
17660         this.store.on('beforeload', this.onBeforeLoad, this);
17661         this.store.on('load', this.onLoad, this);
17662         this.store.on('loadexception', this.onLoadException, this);
17663         /*
17664         if(this.resizable){
17665             this.resizer = new Roo.Resizable(this.list,  {
17666                pinned:true, handles:'se'
17667             });
17668             this.resizer.on('resize', function(r, w, h){
17669                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17670                 this.listWidth = w;
17671                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17672                 this.restrictHeight();
17673             }, this);
17674             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17675         }
17676         */
17677         if(!this.editable){
17678             this.editable = true;
17679             this.setEditable(false);
17680         }
17681         
17682         /*
17683         
17684         if (typeof(this.events.add.listeners) != 'undefined') {
17685             
17686             this.addicon = this.wrap.createChild(
17687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17688        
17689             this.addicon.on('click', function(e) {
17690                 this.fireEvent('add', this);
17691             }, this);
17692         }
17693         if (typeof(this.events.edit.listeners) != 'undefined') {
17694             
17695             this.editicon = this.wrap.createChild(
17696                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17697             if (this.addicon) {
17698                 this.editicon.setStyle('margin-left', '40px');
17699             }
17700             this.editicon.on('click', function(e) {
17701                 
17702                 // we fire even  if inothing is selected..
17703                 this.fireEvent('edit', this, this.lastData );
17704                 
17705             }, this);
17706         }
17707         */
17708         
17709         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17710             "up" : function(e){
17711                 this.inKeyMode = true;
17712                 this.selectPrev();
17713             },
17714
17715             "down" : function(e){
17716                 if(!this.isExpanded()){
17717                     this.onTriggerClick();
17718                 }else{
17719                     this.inKeyMode = true;
17720                     this.selectNext();
17721                 }
17722             },
17723
17724             "enter" : function(e){
17725 //                this.onViewClick();
17726                 //return true;
17727                 this.collapse();
17728                 
17729                 if(this.fireEvent("specialkey", this, e)){
17730                     this.onViewClick(false);
17731                 }
17732                 
17733                 return true;
17734             },
17735
17736             "esc" : function(e){
17737                 this.collapse();
17738             },
17739
17740             "tab" : function(e){
17741                 this.collapse();
17742                 
17743                 if(this.fireEvent("specialkey", this, e)){
17744                     this.onViewClick(false);
17745                 }
17746                 
17747                 return true;
17748             },
17749
17750             scope : this,
17751
17752             doRelay : function(foo, bar, hname){
17753                 if(hname == 'down' || this.scope.isExpanded()){
17754                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17755                 }
17756                 return true;
17757             },
17758
17759             forceKeyDown: true
17760         });
17761         
17762         
17763         this.queryDelay = Math.max(this.queryDelay || 10,
17764                 this.mode == 'local' ? 10 : 250);
17765         
17766         
17767         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17768         
17769         if(this.typeAhead){
17770             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17771         }
17772         if(this.editable !== false){
17773             this.inputEl().on("keyup", this.onKeyUp, this);
17774         }
17775         if(this.forceSelection){
17776             this.inputEl().on('blur', this.doForce, this);
17777         }
17778         
17779         if(this.multiple){
17780             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17781             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17782         }
17783     },
17784     
17785     initTickableEvents: function()
17786     {   
17787         this.createList();
17788         
17789         if(this.hiddenName){
17790             
17791             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17792             
17793             this.hiddenField.dom.value =
17794                 this.hiddenValue !== undefined ? this.hiddenValue :
17795                 this.value !== undefined ? this.value : '';
17796
17797             // prevent input submission
17798             this.el.dom.removeAttribute('name');
17799             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17800              
17801              
17802         }
17803         
17804 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17805         
17806         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17807         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17808         if(this.triggerList){
17809             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17810         }
17811          
17812         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17813         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17814         
17815         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17816         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17817         
17818         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17819         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17820         
17821         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17822         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17823         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17824         
17825         this.okBtn.hide();
17826         this.cancelBtn.hide();
17827         
17828         var _this = this;
17829         
17830         (function(){
17831             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17832             _this.list.setWidth(lw);
17833         }).defer(100);
17834         
17835         this.list.on('mouseover', this.onViewOver, this);
17836         this.list.on('mousemove', this.onViewMove, this);
17837         
17838         this.list.on('scroll', this.onViewScroll, this);
17839         
17840         if(!this.tpl){
17841             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17842                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17843         }
17844
17845         this.view = new Roo.View(this.list, this.tpl, {
17846             singleSelect:true,
17847             tickable:true,
17848             parent:this,
17849             store: this.store,
17850             selectedClass: this.selectedClass
17851         });
17852         
17853         //this.view.wrapEl.setDisplayed(false);
17854         this.view.on('click', this.onViewClick, this);
17855         
17856         
17857         
17858         this.store.on('beforeload', this.onBeforeLoad, this);
17859         this.store.on('load', this.onLoad, this);
17860         this.store.on('loadexception', this.onLoadException, this);
17861         
17862         if(this.editable){
17863             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17864                 "up" : function(e){
17865                     this.inKeyMode = true;
17866                     this.selectPrev();
17867                 },
17868
17869                 "down" : function(e){
17870                     this.inKeyMode = true;
17871                     this.selectNext();
17872                 },
17873
17874                 "enter" : function(e){
17875                     if(this.fireEvent("specialkey", this, e)){
17876                         this.onViewClick(false);
17877                     }
17878                     
17879                     return true;
17880                 },
17881
17882                 "esc" : function(e){
17883                     this.onTickableFooterButtonClick(e, false, false);
17884                 },
17885
17886                 "tab" : function(e){
17887                     this.fireEvent("specialkey", this, e);
17888                     
17889                     this.onTickableFooterButtonClick(e, false, false);
17890                     
17891                     return true;
17892                 },
17893
17894                 scope : this,
17895
17896                 doRelay : function(e, fn, key){
17897                     if(this.scope.isExpanded()){
17898                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17899                     }
17900                     return true;
17901                 },
17902
17903                 forceKeyDown: true
17904             });
17905         }
17906         
17907         this.queryDelay = Math.max(this.queryDelay || 10,
17908                 this.mode == 'local' ? 10 : 250);
17909         
17910         
17911         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17912         
17913         if(this.typeAhead){
17914             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17915         }
17916         
17917         if(this.editable !== false){
17918             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17919         }
17920         
17921         this.indicator = this.indicatorEl();
17922         
17923         if(this.indicator){
17924             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17925             this.indicator.hide();
17926         }
17927         
17928     },
17929
17930     onDestroy : function(){
17931         if(this.view){
17932             this.view.setStore(null);
17933             this.view.el.removeAllListeners();
17934             this.view.el.remove();
17935             this.view.purgeListeners();
17936         }
17937         if(this.list){
17938             this.list.dom.innerHTML  = '';
17939         }
17940         
17941         if(this.store){
17942             this.store.un('beforeload', this.onBeforeLoad, this);
17943             this.store.un('load', this.onLoad, this);
17944             this.store.un('loadexception', this.onLoadException, this);
17945         }
17946         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17947     },
17948
17949     // private
17950     fireKey : function(e){
17951         if(e.isNavKeyPress() && !this.list.isVisible()){
17952             this.fireEvent("specialkey", this, e);
17953         }
17954     },
17955
17956     // private
17957     onResize: function(w, h)
17958     {
17959         
17960         
17961 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17962 //        
17963 //        if(typeof w != 'number'){
17964 //            // we do not handle it!?!?
17965 //            return;
17966 //        }
17967 //        var tw = this.trigger.getWidth();
17968 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17969 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17970 //        var x = w - tw;
17971 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17972 //            
17973 //        //this.trigger.setStyle('left', x+'px');
17974 //        
17975 //        if(this.list && this.listWidth === undefined){
17976 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17977 //            this.list.setWidth(lw);
17978 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17979 //        }
17980         
17981     
17982         
17983     },
17984
17985     /**
17986      * Allow or prevent the user from directly editing the field text.  If false is passed,
17987      * the user will only be able to select from the items defined in the dropdown list.  This method
17988      * is the runtime equivalent of setting the 'editable' config option at config time.
17989      * @param {Boolean} value True to allow the user to directly edit the field text
17990      */
17991     setEditable : function(value){
17992         if(value == this.editable){
17993             return;
17994         }
17995         this.editable = value;
17996         if(!value){
17997             this.inputEl().dom.setAttribute('readOnly', true);
17998             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17999             this.inputEl().addClass('x-combo-noedit');
18000         }else{
18001             this.inputEl().dom.removeAttribute('readOnly');
18002             this.inputEl().un('mousedown', this.onTriggerClick,  this);
18003             this.inputEl().removeClass('x-combo-noedit');
18004         }
18005     },
18006
18007     // private
18008     
18009     onBeforeLoad : function(combo,opts){
18010         if(!this.hasFocus){
18011             return;
18012         }
18013          if (!opts.add) {
18014             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18015          }
18016         this.restrictHeight();
18017         this.selectedIndex = -1;
18018     },
18019
18020     // private
18021     onLoad : function(){
18022         
18023         this.hasQuery = false;
18024         
18025         if(!this.hasFocus){
18026             return;
18027         }
18028         
18029         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18030             this.loading.hide();
18031         }
18032         
18033         if(this.store.getCount() > 0){
18034             
18035             this.expand();
18036             this.restrictHeight();
18037             if(this.lastQuery == this.allQuery){
18038                 if(this.editable && !this.tickable){
18039                     this.inputEl().dom.select();
18040                 }
18041                 
18042                 if(
18043                     !this.selectByValue(this.value, true) &&
18044                     this.autoFocus && 
18045                     (
18046                         !this.store.lastOptions ||
18047                         typeof(this.store.lastOptions.add) == 'undefined' || 
18048                         this.store.lastOptions.add != true
18049                     )
18050                 ){
18051                     this.select(0, true);
18052                 }
18053             }else{
18054                 if(this.autoFocus){
18055                     this.selectNext();
18056                 }
18057                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18058                     this.taTask.delay(this.typeAheadDelay);
18059                 }
18060             }
18061         }else{
18062             this.onEmptyResults();
18063         }
18064         
18065         //this.el.focus();
18066     },
18067     // private
18068     onLoadException : function()
18069     {
18070         this.hasQuery = false;
18071         
18072         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18073             this.loading.hide();
18074         }
18075         
18076         if(this.tickable && this.editable){
18077             return;
18078         }
18079         
18080         this.collapse();
18081         // only causes errors at present
18082         //Roo.log(this.store.reader.jsonData);
18083         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18084             // fixme
18085             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18086         //}
18087         
18088         
18089     },
18090     // private
18091     onTypeAhead : function(){
18092         if(this.store.getCount() > 0){
18093             var r = this.store.getAt(0);
18094             var newValue = r.data[this.displayField];
18095             var len = newValue.length;
18096             var selStart = this.getRawValue().length;
18097             
18098             if(selStart != len){
18099                 this.setRawValue(newValue);
18100                 this.selectText(selStart, newValue.length);
18101             }
18102         }
18103     },
18104
18105     // private
18106     onSelect : function(record, index){
18107         
18108         if(this.fireEvent('beforeselect', this, record, index) !== false){
18109         
18110             this.setFromData(index > -1 ? record.data : false);
18111             
18112             this.collapse();
18113             this.fireEvent('select', this, record, index);
18114         }
18115     },
18116
18117     /**
18118      * Returns the currently selected field value or empty string if no value is set.
18119      * @return {String} value The selected value
18120      */
18121     getValue : function()
18122     {
18123         if(Roo.isIOS && this.useNativeIOS){
18124             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18125         }
18126         
18127         if(this.multiple){
18128             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18129         }
18130         
18131         if(this.valueField){
18132             return typeof this.value != 'undefined' ? this.value : '';
18133         }else{
18134             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18135         }
18136     },
18137     
18138     getRawValue : function()
18139     {
18140         if(Roo.isIOS && this.useNativeIOS){
18141             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18142         }
18143         
18144         var v = this.inputEl().getValue();
18145         
18146         return v;
18147     },
18148
18149     /**
18150      * Clears any text/value currently set in the field
18151      */
18152     clearValue : function(){
18153         
18154         if(this.hiddenField){
18155             this.hiddenField.dom.value = '';
18156         }
18157         this.value = '';
18158         this.setRawValue('');
18159         this.lastSelectionText = '';
18160         this.lastData = false;
18161         
18162         var close = this.closeTriggerEl();
18163         
18164         if(close){
18165             close.hide();
18166         }
18167         
18168         this.validate();
18169         
18170     },
18171
18172     /**
18173      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18174      * will be displayed in the field.  If the value does not match the data value of an existing item,
18175      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18176      * Otherwise the field will be blank (although the value will still be set).
18177      * @param {String} value The value to match
18178      */
18179     setValue : function(v)
18180     {
18181         if(Roo.isIOS && this.useNativeIOS){
18182             this.setIOSValue(v);
18183             return;
18184         }
18185         
18186         if(this.multiple){
18187             this.syncValue();
18188             return;
18189         }
18190         
18191         var text = v;
18192         if(this.valueField){
18193             var r = this.findRecord(this.valueField, v);
18194             if(r){
18195                 text = r.data[this.displayField];
18196             }else if(this.valueNotFoundText !== undefined){
18197                 text = this.valueNotFoundText;
18198             }
18199         }
18200         this.lastSelectionText = text;
18201         if(this.hiddenField){
18202             this.hiddenField.dom.value = v;
18203         }
18204         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18205         this.value = v;
18206         
18207         var close = this.closeTriggerEl();
18208         
18209         if(close){
18210             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18211         }
18212         
18213         this.validate();
18214     },
18215     /**
18216      * @property {Object} the last set data for the element
18217      */
18218     
18219     lastData : false,
18220     /**
18221      * Sets the value of the field based on a object which is related to the record format for the store.
18222      * @param {Object} value the value to set as. or false on reset?
18223      */
18224     setFromData : function(o){
18225         
18226         if(this.multiple){
18227             this.addItem(o);
18228             return;
18229         }
18230             
18231         var dv = ''; // display value
18232         var vv = ''; // value value..
18233         this.lastData = o;
18234         if (this.displayField) {
18235             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18236         } else {
18237             // this is an error condition!!!
18238             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18239         }
18240         
18241         if(this.valueField){
18242             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18243         }
18244         
18245         var close = this.closeTriggerEl();
18246         
18247         if(close){
18248             if(dv.length || vv * 1 > 0){
18249                 close.show() ;
18250                 this.blockFocus=true;
18251             } else {
18252                 close.hide();
18253             }             
18254         }
18255         
18256         if(this.hiddenField){
18257             this.hiddenField.dom.value = vv;
18258             
18259             this.lastSelectionText = dv;
18260             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18261             this.value = vv;
18262             return;
18263         }
18264         // no hidden field.. - we store the value in 'value', but still display
18265         // display field!!!!
18266         this.lastSelectionText = dv;
18267         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18268         this.value = vv;
18269         
18270         
18271         
18272     },
18273     // private
18274     reset : function(){
18275         // overridden so that last data is reset..
18276         
18277         if(this.multiple){
18278             this.clearItem();
18279             return;
18280         }
18281         
18282         this.setValue(this.originalValue);
18283         //this.clearInvalid();
18284         this.lastData = false;
18285         if (this.view) {
18286             this.view.clearSelections();
18287         }
18288         
18289         this.validate();
18290     },
18291     // private
18292     findRecord : function(prop, value){
18293         var record;
18294         if(this.store.getCount() > 0){
18295             this.store.each(function(r){
18296                 if(r.data[prop] == value){
18297                     record = r;
18298                     return false;
18299                 }
18300                 return true;
18301             });
18302         }
18303         return record;
18304     },
18305     
18306     getName: function()
18307     {
18308         // returns hidden if it's set..
18309         if (!this.rendered) {return ''};
18310         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18311         
18312     },
18313     // private
18314     onViewMove : function(e, t){
18315         this.inKeyMode = false;
18316     },
18317
18318     // private
18319     onViewOver : function(e, t){
18320         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18321             return;
18322         }
18323         var item = this.view.findItemFromChild(t);
18324         
18325         if(item){
18326             var index = this.view.indexOf(item);
18327             this.select(index, false);
18328         }
18329     },
18330
18331     // private
18332     onViewClick : function(view, doFocus, el, e)
18333     {
18334         var index = this.view.getSelectedIndexes()[0];
18335         
18336         var r = this.store.getAt(index);
18337         
18338         if(this.tickable){
18339             
18340             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18341                 return;
18342             }
18343             
18344             var rm = false;
18345             var _this = this;
18346             
18347             Roo.each(this.tickItems, function(v,k){
18348                 
18349                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18350                     Roo.log(v);
18351                     _this.tickItems.splice(k, 1);
18352                     
18353                     if(typeof(e) == 'undefined' && view == false){
18354                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18355                     }
18356                     
18357                     rm = true;
18358                     return;
18359                 }
18360             });
18361             
18362             if(rm){
18363                 return;
18364             }
18365             
18366             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18367                 this.tickItems.push(r.data);
18368             }
18369             
18370             if(typeof(e) == 'undefined' && view == false){
18371                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18372             }
18373                     
18374             return;
18375         }
18376         
18377         if(r){
18378             this.onSelect(r, index);
18379         }
18380         if(doFocus !== false && !this.blockFocus){
18381             this.inputEl().focus();
18382         }
18383     },
18384
18385     // private
18386     restrictHeight : function(){
18387         //this.innerList.dom.style.height = '';
18388         //var inner = this.innerList.dom;
18389         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18390         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18391         //this.list.beginUpdate();
18392         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18393         this.list.alignTo(this.inputEl(), this.listAlign);
18394         this.list.alignTo(this.inputEl(), this.listAlign);
18395         //this.list.endUpdate();
18396     },
18397
18398     // private
18399     onEmptyResults : function(){
18400         
18401         if(this.tickable && this.editable){
18402             this.hasFocus = false;
18403             this.restrictHeight();
18404             return;
18405         }
18406         
18407         this.collapse();
18408     },
18409
18410     /**
18411      * Returns true if the dropdown list is expanded, else false.
18412      */
18413     isExpanded : function(){
18414         return this.list.isVisible();
18415     },
18416
18417     /**
18418      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18419      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18420      * @param {String} value The data value of the item to select
18421      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18422      * selected item if it is not currently in view (defaults to true)
18423      * @return {Boolean} True if the value matched an item in the list, else false
18424      */
18425     selectByValue : function(v, scrollIntoView){
18426         if(v !== undefined && v !== null){
18427             var r = this.findRecord(this.valueField || this.displayField, v);
18428             if(r){
18429                 this.select(this.store.indexOf(r), scrollIntoView);
18430                 return true;
18431             }
18432         }
18433         return false;
18434     },
18435
18436     /**
18437      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18439      * @param {Number} index The zero-based index of the list item to select
18440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18441      * selected item if it is not currently in view (defaults to true)
18442      */
18443     select : function(index, scrollIntoView){
18444         this.selectedIndex = index;
18445         this.view.select(index);
18446         if(scrollIntoView !== false){
18447             var el = this.view.getNode(index);
18448             /*
18449              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18450              */
18451             if(el){
18452                 this.list.scrollChildIntoView(el, false);
18453             }
18454         }
18455     },
18456
18457     // private
18458     selectNext : function(){
18459         var ct = this.store.getCount();
18460         if(ct > 0){
18461             if(this.selectedIndex == -1){
18462                 this.select(0);
18463             }else if(this.selectedIndex < ct-1){
18464                 this.select(this.selectedIndex+1);
18465             }
18466         }
18467     },
18468
18469     // private
18470     selectPrev : function(){
18471         var ct = this.store.getCount();
18472         if(ct > 0){
18473             if(this.selectedIndex == -1){
18474                 this.select(0);
18475             }else if(this.selectedIndex != 0){
18476                 this.select(this.selectedIndex-1);
18477             }
18478         }
18479     },
18480
18481     // private
18482     onKeyUp : function(e){
18483         if(this.editable !== false && !e.isSpecialKey()){
18484             this.lastKey = e.getKey();
18485             this.dqTask.delay(this.queryDelay);
18486         }
18487     },
18488
18489     // private
18490     validateBlur : function(){
18491         return !this.list || !this.list.isVisible();   
18492     },
18493
18494     // private
18495     initQuery : function(){
18496         
18497         var v = this.getRawValue();
18498         
18499         if(this.tickable && this.editable){
18500             v = this.tickableInputEl().getValue();
18501         }
18502         
18503         this.doQuery(v);
18504     },
18505
18506     // private
18507     doForce : function(){
18508         if(this.inputEl().dom.value.length > 0){
18509             this.inputEl().dom.value =
18510                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18511              
18512         }
18513     },
18514
18515     /**
18516      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18517      * query allowing the query action to be canceled if needed.
18518      * @param {String} query The SQL query to execute
18519      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18520      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18521      * saved in the current store (defaults to false)
18522      */
18523     doQuery : function(q, forceAll){
18524         
18525         if(q === undefined || q === null){
18526             q = '';
18527         }
18528         var qe = {
18529             query: q,
18530             forceAll: forceAll,
18531             combo: this,
18532             cancel:false
18533         };
18534         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18535             return false;
18536         }
18537         q = qe.query;
18538         
18539         forceAll = qe.forceAll;
18540         if(forceAll === true || (q.length >= this.minChars)){
18541             
18542             this.hasQuery = true;
18543             
18544             if(this.lastQuery != q || this.alwaysQuery){
18545                 this.lastQuery = q;
18546                 if(this.mode == 'local'){
18547                     this.selectedIndex = -1;
18548                     if(forceAll){
18549                         this.store.clearFilter();
18550                     }else{
18551                         
18552                         if(this.specialFilter){
18553                             this.fireEvent('specialfilter', this);
18554                             this.onLoad();
18555                             return;
18556                         }
18557                         
18558                         this.store.filter(this.displayField, q);
18559                     }
18560                     
18561                     this.store.fireEvent("datachanged", this.store);
18562                     
18563                     this.onLoad();
18564                     
18565                     
18566                 }else{
18567                     
18568                     this.store.baseParams[this.queryParam] = q;
18569                     
18570                     var options = {params : this.getParams(q)};
18571                     
18572                     if(this.loadNext){
18573                         options.add = true;
18574                         options.params.start = this.page * this.pageSize;
18575                     }
18576                     
18577                     this.store.load(options);
18578                     
18579                     /*
18580                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18581                      *  we should expand the list on onLoad
18582                      *  so command out it
18583                      */
18584 //                    this.expand();
18585                 }
18586             }else{
18587                 this.selectedIndex = -1;
18588                 this.onLoad();   
18589             }
18590         }
18591         
18592         this.loadNext = false;
18593     },
18594     
18595     // private
18596     getParams : function(q){
18597         var p = {};
18598         //p[this.queryParam] = q;
18599         
18600         if(this.pageSize){
18601             p.start = 0;
18602             p.limit = this.pageSize;
18603         }
18604         return p;
18605     },
18606
18607     /**
18608      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18609      */
18610     collapse : function(){
18611         if(!this.isExpanded()){
18612             return;
18613         }
18614         
18615         this.list.hide();
18616         
18617         this.hasFocus = false;
18618         
18619         if(this.tickable){
18620             this.okBtn.hide();
18621             this.cancelBtn.hide();
18622             this.trigger.show();
18623             
18624             if(this.editable){
18625                 this.tickableInputEl().dom.value = '';
18626                 this.tickableInputEl().blur();
18627             }
18628             
18629         }
18630         
18631         Roo.get(document).un('mousedown', this.collapseIf, this);
18632         Roo.get(document).un('mousewheel', this.collapseIf, this);
18633         if (!this.editable) {
18634             Roo.get(document).un('keydown', this.listKeyPress, this);
18635         }
18636         this.fireEvent('collapse', this);
18637         
18638         this.validate();
18639     },
18640
18641     // private
18642     collapseIf : function(e){
18643         var in_combo  = e.within(this.el);
18644         var in_list =  e.within(this.list);
18645         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18646         
18647         if (in_combo || in_list || is_list) {
18648             //e.stopPropagation();
18649             return;
18650         }
18651         
18652         if(this.tickable){
18653             this.onTickableFooterButtonClick(e, false, false);
18654         }
18655
18656         this.collapse();
18657         
18658     },
18659
18660     /**
18661      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18662      */
18663     expand : function(){
18664        
18665         if(this.isExpanded() || !this.hasFocus){
18666             return;
18667         }
18668         
18669         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18670         this.list.setWidth(lw);
18671         
18672         Roo.log('expand');
18673         
18674         this.list.show();
18675         
18676         this.restrictHeight();
18677         
18678         if(this.tickable){
18679             
18680             this.tickItems = Roo.apply([], this.item);
18681             
18682             this.okBtn.show();
18683             this.cancelBtn.show();
18684             this.trigger.hide();
18685             
18686             if(this.editable){
18687                 this.tickableInputEl().focus();
18688             }
18689             
18690         }
18691         
18692         Roo.get(document).on('mousedown', this.collapseIf, this);
18693         Roo.get(document).on('mousewheel', this.collapseIf, this);
18694         if (!this.editable) {
18695             Roo.get(document).on('keydown', this.listKeyPress, this);
18696         }
18697         
18698         this.fireEvent('expand', this);
18699     },
18700
18701     // private
18702     // Implements the default empty TriggerField.onTriggerClick function
18703     onTriggerClick : function(e)
18704     {
18705         Roo.log('trigger click');
18706         
18707         if(this.disabled || !this.triggerList){
18708             return;
18709         }
18710         
18711         this.page = 0;
18712         this.loadNext = false;
18713         
18714         if(this.isExpanded()){
18715             this.collapse();
18716             if (!this.blockFocus) {
18717                 this.inputEl().focus();
18718             }
18719             
18720         }else {
18721             this.hasFocus = true;
18722             if(this.triggerAction == 'all') {
18723                 this.doQuery(this.allQuery, true);
18724             } else {
18725                 this.doQuery(this.getRawValue());
18726             }
18727             if (!this.blockFocus) {
18728                 this.inputEl().focus();
18729             }
18730         }
18731     },
18732     
18733     onTickableTriggerClick : function(e)
18734     {
18735         if(this.disabled){
18736             return;
18737         }
18738         
18739         this.page = 0;
18740         this.loadNext = false;
18741         this.hasFocus = true;
18742         
18743         if(this.triggerAction == 'all') {
18744             this.doQuery(this.allQuery, true);
18745         } else {
18746             this.doQuery(this.getRawValue());
18747         }
18748     },
18749     
18750     onSearchFieldClick : function(e)
18751     {
18752         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18753             this.onTickableFooterButtonClick(e, false, false);
18754             return;
18755         }
18756         
18757         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18758             return;
18759         }
18760         
18761         this.page = 0;
18762         this.loadNext = false;
18763         this.hasFocus = true;
18764         
18765         if(this.triggerAction == 'all') {
18766             this.doQuery(this.allQuery, true);
18767         } else {
18768             this.doQuery(this.getRawValue());
18769         }
18770     },
18771     
18772     listKeyPress : function(e)
18773     {
18774         //Roo.log('listkeypress');
18775         // scroll to first matching element based on key pres..
18776         if (e.isSpecialKey()) {
18777             return false;
18778         }
18779         var k = String.fromCharCode(e.getKey()).toUpperCase();
18780         //Roo.log(k);
18781         var match  = false;
18782         var csel = this.view.getSelectedNodes();
18783         var cselitem = false;
18784         if (csel.length) {
18785             var ix = this.view.indexOf(csel[0]);
18786             cselitem  = this.store.getAt(ix);
18787             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18788                 cselitem = false;
18789             }
18790             
18791         }
18792         
18793         this.store.each(function(v) { 
18794             if (cselitem) {
18795                 // start at existing selection.
18796                 if (cselitem.id == v.id) {
18797                     cselitem = false;
18798                 }
18799                 return true;
18800             }
18801                 
18802             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18803                 match = this.store.indexOf(v);
18804                 return false;
18805             }
18806             return true;
18807         }, this);
18808         
18809         if (match === false) {
18810             return true; // no more action?
18811         }
18812         // scroll to?
18813         this.view.select(match);
18814         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18815         sn.scrollIntoView(sn.dom.parentNode, false);
18816     },
18817     
18818     onViewScroll : function(e, t){
18819         
18820         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){
18821             return;
18822         }
18823         
18824         this.hasQuery = true;
18825         
18826         this.loading = this.list.select('.loading', true).first();
18827         
18828         if(this.loading === null){
18829             this.list.createChild({
18830                 tag: 'div',
18831                 cls: 'loading roo-select2-more-results roo-select2-active',
18832                 html: 'Loading more results...'
18833             });
18834             
18835             this.loading = this.list.select('.loading', true).first();
18836             
18837             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18838             
18839             this.loading.hide();
18840         }
18841         
18842         this.loading.show();
18843         
18844         var _combo = this;
18845         
18846         this.page++;
18847         this.loadNext = true;
18848         
18849         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18850         
18851         return;
18852     },
18853     
18854     addItem : function(o)
18855     {   
18856         var dv = ''; // display value
18857         
18858         if (this.displayField) {
18859             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18860         } else {
18861             // this is an error condition!!!
18862             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18863         }
18864         
18865         if(!dv.length){
18866             return;
18867         }
18868         
18869         var choice = this.choices.createChild({
18870             tag: 'li',
18871             cls: 'roo-select2-search-choice',
18872             cn: [
18873                 {
18874                     tag: 'div',
18875                     html: dv
18876                 },
18877                 {
18878                     tag: 'a',
18879                     href: '#',
18880                     cls: 'roo-select2-search-choice-close fa fa-times',
18881                     tabindex: '-1'
18882                 }
18883             ]
18884             
18885         }, this.searchField);
18886         
18887         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18888         
18889         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18890         
18891         this.item.push(o);
18892         
18893         this.lastData = o;
18894         
18895         this.syncValue();
18896         
18897         this.inputEl().dom.value = '';
18898         
18899         this.validate();
18900     },
18901     
18902     onRemoveItem : function(e, _self, o)
18903     {
18904         e.preventDefault();
18905         
18906         this.lastItem = Roo.apply([], this.item);
18907         
18908         var index = this.item.indexOf(o.data) * 1;
18909         
18910         if( index < 0){
18911             Roo.log('not this item?!');
18912             return;
18913         }
18914         
18915         this.item.splice(index, 1);
18916         o.item.remove();
18917         
18918         this.syncValue();
18919         
18920         this.fireEvent('remove', this, e);
18921         
18922         this.validate();
18923         
18924     },
18925     
18926     syncValue : function()
18927     {
18928         if(!this.item.length){
18929             this.clearValue();
18930             return;
18931         }
18932             
18933         var value = [];
18934         var _this = this;
18935         Roo.each(this.item, function(i){
18936             if(_this.valueField){
18937                 value.push(i[_this.valueField]);
18938                 return;
18939             }
18940
18941             value.push(i);
18942         });
18943
18944         this.value = value.join(',');
18945
18946         if(this.hiddenField){
18947             this.hiddenField.dom.value = this.value;
18948         }
18949         
18950         this.store.fireEvent("datachanged", this.store);
18951         
18952         this.validate();
18953     },
18954     
18955     clearItem : function()
18956     {
18957         if(!this.multiple){
18958             return;
18959         }
18960         
18961         this.item = [];
18962         
18963         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18964            c.remove();
18965         });
18966         
18967         this.syncValue();
18968         
18969         this.validate();
18970         
18971         if(this.tickable && !Roo.isTouch){
18972             this.view.refresh();
18973         }
18974     },
18975     
18976     inputEl: function ()
18977     {
18978         if(Roo.isIOS && this.useNativeIOS){
18979             return this.el.select('select.roo-ios-select', true).first();
18980         }
18981         
18982         if(Roo.isTouch && this.mobileTouchView){
18983             return this.el.select('input.form-control',true).first();
18984         }
18985         
18986         if(this.tickable){
18987             return this.searchField;
18988         }
18989         
18990         return this.el.select('input.form-control',true).first();
18991     },
18992     
18993     onTickableFooterButtonClick : function(e, btn, el)
18994     {
18995         e.preventDefault();
18996         
18997         this.lastItem = Roo.apply([], this.item);
18998         
18999         if(btn && btn.name == 'cancel'){
19000             this.tickItems = Roo.apply([], this.item);
19001             this.collapse();
19002             return;
19003         }
19004         
19005         this.clearItem();
19006         
19007         var _this = this;
19008         
19009         Roo.each(this.tickItems, function(o){
19010             _this.addItem(o);
19011         });
19012         
19013         this.collapse();
19014         
19015     },
19016     
19017     validate : function()
19018     {
19019         if(this.getVisibilityEl().hasClass('hidden')){
19020             return true;
19021         }
19022         
19023         var v = this.getRawValue();
19024         
19025         if(this.multiple){
19026             v = this.getValue();
19027         }
19028         
19029         if(this.disabled || this.allowBlank || v.length){
19030             this.markValid();
19031             return true;
19032         }
19033         
19034         this.markInvalid();
19035         return false;
19036     },
19037     
19038     tickableInputEl : function()
19039     {
19040         if(!this.tickable || !this.editable){
19041             return this.inputEl();
19042         }
19043         
19044         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19045     },
19046     
19047     
19048     getAutoCreateTouchView : function()
19049     {
19050         var id = Roo.id();
19051         
19052         var cfg = {
19053             cls: 'form-group' //input-group
19054         };
19055         
19056         var input =  {
19057             tag: 'input',
19058             id : id,
19059             type : this.inputType,
19060             cls : 'form-control x-combo-noedit',
19061             autocomplete: 'new-password',
19062             placeholder : this.placeholder || '',
19063             readonly : true
19064         };
19065         
19066         if (this.name) {
19067             input.name = this.name;
19068         }
19069         
19070         if (this.size) {
19071             input.cls += ' input-' + this.size;
19072         }
19073         
19074         if (this.disabled) {
19075             input.disabled = true;
19076         }
19077         
19078         var inputblock = {
19079             cls : 'roo-combobox-wrap',
19080             cn : [
19081                 input
19082             ]
19083         };
19084         
19085         if(this.before){
19086             inputblock.cls += ' input-group';
19087             
19088             inputblock.cn.unshift({
19089                 tag :'span',
19090                 cls : 'input-group-addon input-group-prepend input-group-text',
19091                 html : this.before
19092             });
19093         }
19094         
19095         if(this.removable && !this.multiple){
19096             inputblock.cls += ' roo-removable';
19097             
19098             inputblock.cn.push({
19099                 tag: 'button',
19100                 html : 'x',
19101                 cls : 'roo-combo-removable-btn close'
19102             });
19103         }
19104
19105         if(this.hasFeedback && !this.allowBlank){
19106             
19107             inputblock.cls += ' has-feedback';
19108             
19109             inputblock.cn.push({
19110                 tag: 'span',
19111                 cls: 'glyphicon form-control-feedback'
19112             });
19113             
19114         }
19115         
19116         if (this.after) {
19117             
19118             inputblock.cls += (this.before) ? '' : ' input-group';
19119             
19120             inputblock.cn.push({
19121                 tag :'span',
19122                 cls : 'input-group-addon input-group-append input-group-text',
19123                 html : this.after
19124             });
19125         }
19126
19127         
19128         var ibwrap = inputblock;
19129         
19130         if(this.multiple){
19131             ibwrap = {
19132                 tag: 'ul',
19133                 cls: 'roo-select2-choices',
19134                 cn:[
19135                     {
19136                         tag: 'li',
19137                         cls: 'roo-select2-search-field',
19138                         cn: [
19139
19140                             inputblock
19141                         ]
19142                     }
19143                 ]
19144             };
19145         
19146             
19147         }
19148         
19149         var combobox = {
19150             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19151             cn: [
19152                 {
19153                     tag: 'input',
19154                     type : 'hidden',
19155                     cls: 'form-hidden-field'
19156                 },
19157                 ibwrap
19158             ]
19159         };
19160         
19161         if(!this.multiple && this.showToggleBtn){
19162             
19163             var caret = {
19164                 cls: 'caret'
19165             };
19166             
19167             if (this.caret != false) {
19168                 caret = {
19169                      tag: 'i',
19170                      cls: 'fa fa-' + this.caret
19171                 };
19172                 
19173             }
19174             
19175             combobox.cn.push({
19176                 tag :'span',
19177                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19178                 cn : [
19179                     Roo.bootstrap.version == 3 ? caret : '',
19180                     {
19181                         tag: 'span',
19182                         cls: 'combobox-clear',
19183                         cn  : [
19184                             {
19185                                 tag : 'i',
19186                                 cls: 'icon-remove'
19187                             }
19188                         ]
19189                     }
19190                 ]
19191
19192             })
19193         }
19194         
19195         if(this.multiple){
19196             combobox.cls += ' roo-select2-container-multi';
19197         }
19198         
19199         var required =  this.allowBlank ?  {
19200                     tag : 'i',
19201                     style: 'display: none'
19202                 } : {
19203                    tag : 'i',
19204                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19205                    tooltip : 'This field is required'
19206                 };
19207         
19208         var align = this.labelAlign || this.parentLabelAlign();
19209         
19210         if (align ==='left' && this.fieldLabel.length) {
19211
19212             cfg.cn = [
19213                 required,
19214                 {
19215                     tag: 'label',
19216                     cls : 'control-label col-form-label',
19217                     html : this.fieldLabel
19218
19219                 },
19220                 {
19221                     cls : 'roo-combobox-wrap ', 
19222                     cn: [
19223                         combobox
19224                     ]
19225                 }
19226             ];
19227             
19228             var labelCfg = cfg.cn[1];
19229             var contentCfg = cfg.cn[2];
19230             
19231
19232             if(this.indicatorpos == 'right'){
19233                 cfg.cn = [
19234                     {
19235                         tag: 'label',
19236                         'for' :  id,
19237                         cls : 'control-label col-form-label',
19238                         cn : [
19239                             {
19240                                 tag : 'span',
19241                                 html : this.fieldLabel
19242                             },
19243                             required
19244                         ]
19245                     },
19246                     {
19247                         cls : "roo-combobox-wrap ",
19248                         cn: [
19249                             combobox
19250                         ]
19251                     }
19252
19253                 ];
19254                 
19255                 labelCfg = cfg.cn[0];
19256                 contentCfg = cfg.cn[1];
19257             }
19258             
19259            
19260             
19261             if(this.labelWidth > 12){
19262                 labelCfg.style = "width: " + this.labelWidth + 'px';
19263             }
19264            
19265             if(this.labelWidth < 13 && this.labelmd == 0){
19266                 this.labelmd = this.labelWidth;
19267             }
19268             
19269             if(this.labellg > 0){
19270                 labelCfg.cls += ' col-lg-' + this.labellg;
19271                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19272             }
19273             
19274             if(this.labelmd > 0){
19275                 labelCfg.cls += ' col-md-' + this.labelmd;
19276                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19277             }
19278             
19279             if(this.labelsm > 0){
19280                 labelCfg.cls += ' col-sm-' + this.labelsm;
19281                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19282             }
19283             
19284             if(this.labelxs > 0){
19285                 labelCfg.cls += ' col-xs-' + this.labelxs;
19286                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19287             }
19288                 
19289                 
19290         } else if ( this.fieldLabel.length) {
19291             cfg.cn = [
19292                required,
19293                 {
19294                     tag: 'label',
19295                     cls : 'control-label',
19296                     html : this.fieldLabel
19297
19298                 },
19299                 {
19300                     cls : '', 
19301                     cn: [
19302                         combobox
19303                     ]
19304                 }
19305             ];
19306             
19307             if(this.indicatorpos == 'right'){
19308                 cfg.cn = [
19309                     {
19310                         tag: 'label',
19311                         cls : 'control-label',
19312                         html : this.fieldLabel,
19313                         cn : [
19314                             required
19315                         ]
19316                     },
19317                     {
19318                         cls : '', 
19319                         cn: [
19320                             combobox
19321                         ]
19322                     }
19323                 ];
19324             }
19325         } else {
19326             cfg.cn = combobox;    
19327         }
19328         
19329         
19330         var settings = this;
19331         
19332         ['xs','sm','md','lg'].map(function(size){
19333             if (settings[size]) {
19334                 cfg.cls += ' col-' + size + '-' + settings[size];
19335             }
19336         });
19337         
19338         return cfg;
19339     },
19340     
19341     initTouchView : function()
19342     {
19343         this.renderTouchView();
19344         
19345         this.touchViewEl.on('scroll', function(){
19346             this.el.dom.scrollTop = 0;
19347         }, this);
19348         
19349         this.originalValue = this.getValue();
19350         
19351         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19352         
19353         this.inputEl().on("click", this.showTouchView, this);
19354         if (this.triggerEl) {
19355             this.triggerEl.on("click", this.showTouchView, this);
19356         }
19357         
19358         
19359         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19360         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19361         
19362         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19363         
19364         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19365         this.store.on('load', this.onTouchViewLoad, this);
19366         this.store.on('loadexception', this.onTouchViewLoadException, this);
19367         
19368         if(this.hiddenName){
19369             
19370             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19371             
19372             this.hiddenField.dom.value =
19373                 this.hiddenValue !== undefined ? this.hiddenValue :
19374                 this.value !== undefined ? this.value : '';
19375         
19376             this.el.dom.removeAttribute('name');
19377             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19378         }
19379         
19380         if(this.multiple){
19381             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19382             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19383         }
19384         
19385         if(this.removable && !this.multiple){
19386             var close = this.closeTriggerEl();
19387             if(close){
19388                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19389                 close.on('click', this.removeBtnClick, this, close);
19390             }
19391         }
19392         /*
19393          * fix the bug in Safari iOS8
19394          */
19395         this.inputEl().on("focus", function(e){
19396             document.activeElement.blur();
19397         }, this);
19398         
19399         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19400         
19401         return;
19402         
19403         
19404     },
19405     
19406     renderTouchView : function()
19407     {
19408         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19409         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19410         
19411         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19412         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19413         
19414         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19415         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19416         this.touchViewBodyEl.setStyle('overflow', 'auto');
19417         
19418         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19419         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19420         
19421         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19422         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19423         
19424     },
19425     
19426     showTouchView : function()
19427     {
19428         if(this.disabled){
19429             return;
19430         }
19431         
19432         this.touchViewHeaderEl.hide();
19433
19434         if(this.modalTitle.length){
19435             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19436             this.touchViewHeaderEl.show();
19437         }
19438
19439         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19440         this.touchViewEl.show();
19441
19442         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19443         
19444         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19445         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19446
19447         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19448
19449         if(this.modalTitle.length){
19450             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19451         }
19452         
19453         this.touchViewBodyEl.setHeight(bodyHeight);
19454
19455         if(this.animate){
19456             var _this = this;
19457             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19458         }else{
19459             this.touchViewEl.addClass(['in','show']);
19460         }
19461         
19462         if(this._touchViewMask){
19463             Roo.get(document.body).addClass("x-body-masked");
19464             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19465             this._touchViewMask.setStyle('z-index', 10000);
19466             this._touchViewMask.addClass('show');
19467         }
19468         
19469         this.doTouchViewQuery();
19470         
19471     },
19472     
19473     hideTouchView : function()
19474     {
19475         this.touchViewEl.removeClass(['in','show']);
19476
19477         if(this.animate){
19478             var _this = this;
19479             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19480         }else{
19481             this.touchViewEl.setStyle('display', 'none');
19482         }
19483         
19484         if(this._touchViewMask){
19485             this._touchViewMask.removeClass('show');
19486             Roo.get(document.body).removeClass("x-body-masked");
19487         }
19488     },
19489     
19490     setTouchViewValue : function()
19491     {
19492         if(this.multiple){
19493             this.clearItem();
19494         
19495             var _this = this;
19496
19497             Roo.each(this.tickItems, function(o){
19498                 this.addItem(o);
19499             }, this);
19500         }
19501         
19502         this.hideTouchView();
19503     },
19504     
19505     doTouchViewQuery : function()
19506     {
19507         var qe = {
19508             query: '',
19509             forceAll: true,
19510             combo: this,
19511             cancel:false
19512         };
19513         
19514         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19515             return false;
19516         }
19517         
19518         if(!this.alwaysQuery || this.mode == 'local'){
19519             this.onTouchViewLoad();
19520             return;
19521         }
19522         
19523         this.store.load();
19524     },
19525     
19526     onTouchViewBeforeLoad : function(combo,opts)
19527     {
19528         return;
19529     },
19530
19531     // private
19532     onTouchViewLoad : function()
19533     {
19534         if(this.store.getCount() < 1){
19535             this.onTouchViewEmptyResults();
19536             return;
19537         }
19538         
19539         this.clearTouchView();
19540         
19541         var rawValue = this.getRawValue();
19542         
19543         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19544         
19545         this.tickItems = [];
19546         
19547         this.store.data.each(function(d, rowIndex){
19548             var row = this.touchViewListGroup.createChild(template);
19549             
19550             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19551                 row.addClass(d.data.cls);
19552             }
19553             
19554             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19555                 var cfg = {
19556                     data : d.data,
19557                     html : d.data[this.displayField]
19558                 };
19559                 
19560                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19561                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19562                 }
19563             }
19564             row.removeClass('selected');
19565             if(!this.multiple && this.valueField &&
19566                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19567             {
19568                 // radio buttons..
19569                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19570                 row.addClass('selected');
19571             }
19572             
19573             if(this.multiple && this.valueField &&
19574                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19575             {
19576                 
19577                 // checkboxes...
19578                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19579                 this.tickItems.push(d.data);
19580             }
19581             
19582             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19583             
19584         }, this);
19585         
19586         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19587         
19588         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19589
19590         if(this.modalTitle.length){
19591             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19592         }
19593
19594         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19595         
19596         if(this.mobile_restrict_height && listHeight < bodyHeight){
19597             this.touchViewBodyEl.setHeight(listHeight);
19598         }
19599         
19600         var _this = this;
19601         
19602         if(firstChecked && listHeight > bodyHeight){
19603             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19604         }
19605         
19606     },
19607     
19608     onTouchViewLoadException : function()
19609     {
19610         this.hideTouchView();
19611     },
19612     
19613     onTouchViewEmptyResults : function()
19614     {
19615         this.clearTouchView();
19616         
19617         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19618         
19619         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19620         
19621     },
19622     
19623     clearTouchView : function()
19624     {
19625         this.touchViewListGroup.dom.innerHTML = '';
19626     },
19627     
19628     onTouchViewClick : function(e, el, o)
19629     {
19630         e.preventDefault();
19631         
19632         var row = o.row;
19633         var rowIndex = o.rowIndex;
19634         
19635         var r = this.store.getAt(rowIndex);
19636         
19637         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19638             
19639             if(!this.multiple){
19640                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19641                     c.dom.removeAttribute('checked');
19642                 }, this);
19643
19644                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19645
19646                 this.setFromData(r.data);
19647
19648                 var close = this.closeTriggerEl();
19649
19650                 if(close){
19651                     close.show();
19652                 }
19653
19654                 this.hideTouchView();
19655
19656                 this.fireEvent('select', this, r, rowIndex);
19657
19658                 return;
19659             }
19660
19661             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19662                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19663                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19664                 return;
19665             }
19666
19667             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19668             this.addItem(r.data);
19669             this.tickItems.push(r.data);
19670         }
19671     },
19672     
19673     getAutoCreateNativeIOS : function()
19674     {
19675         var cfg = {
19676             cls: 'form-group' //input-group,
19677         };
19678         
19679         var combobox =  {
19680             tag: 'select',
19681             cls : 'roo-ios-select'
19682         };
19683         
19684         if (this.name) {
19685             combobox.name = this.name;
19686         }
19687         
19688         if (this.disabled) {
19689             combobox.disabled = true;
19690         }
19691         
19692         var settings = this;
19693         
19694         ['xs','sm','md','lg'].map(function(size){
19695             if (settings[size]) {
19696                 cfg.cls += ' col-' + size + '-' + settings[size];
19697             }
19698         });
19699         
19700         cfg.cn = combobox;
19701         
19702         return cfg;
19703         
19704     },
19705     
19706     initIOSView : function()
19707     {
19708         this.store.on('load', this.onIOSViewLoad, this);
19709         
19710         return;
19711     },
19712     
19713     onIOSViewLoad : function()
19714     {
19715         if(this.store.getCount() < 1){
19716             return;
19717         }
19718         
19719         this.clearIOSView();
19720         
19721         if(this.allowBlank) {
19722             
19723             var default_text = '-- SELECT --';
19724             
19725             if(this.placeholder.length){
19726                 default_text = this.placeholder;
19727             }
19728             
19729             if(this.emptyTitle.length){
19730                 default_text += ' - ' + this.emptyTitle + ' -';
19731             }
19732             
19733             var opt = this.inputEl().createChild({
19734                 tag: 'option',
19735                 value : 0,
19736                 html : default_text
19737             });
19738             
19739             var o = {};
19740             o[this.valueField] = 0;
19741             o[this.displayField] = default_text;
19742             
19743             this.ios_options.push({
19744                 data : o,
19745                 el : opt
19746             });
19747             
19748         }
19749         
19750         this.store.data.each(function(d, rowIndex){
19751             
19752             var html = '';
19753             
19754             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19755                 html = d.data[this.displayField];
19756             }
19757             
19758             var value = '';
19759             
19760             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19761                 value = d.data[this.valueField];
19762             }
19763             
19764             var option = {
19765                 tag: 'option',
19766                 value : value,
19767                 html : html
19768             };
19769             
19770             if(this.value == d.data[this.valueField]){
19771                 option['selected'] = true;
19772             }
19773             
19774             var opt = this.inputEl().createChild(option);
19775             
19776             this.ios_options.push({
19777                 data : d.data,
19778                 el : opt
19779             });
19780             
19781         }, this);
19782         
19783         this.inputEl().on('change', function(){
19784            this.fireEvent('select', this);
19785         }, this);
19786         
19787     },
19788     
19789     clearIOSView: function()
19790     {
19791         this.inputEl().dom.innerHTML = '';
19792         
19793         this.ios_options = [];
19794     },
19795     
19796     setIOSValue: function(v)
19797     {
19798         this.value = v;
19799         
19800         if(!this.ios_options){
19801             return;
19802         }
19803         
19804         Roo.each(this.ios_options, function(opts){
19805            
19806            opts.el.dom.removeAttribute('selected');
19807            
19808            if(opts.data[this.valueField] != v){
19809                return;
19810            }
19811            
19812            opts.el.dom.setAttribute('selected', true);
19813            
19814         }, this);
19815     }
19816
19817     /** 
19818     * @cfg {Boolean} grow 
19819     * @hide 
19820     */
19821     /** 
19822     * @cfg {Number} growMin 
19823     * @hide 
19824     */
19825     /** 
19826     * @cfg {Number} growMax 
19827     * @hide 
19828     */
19829     /**
19830      * @hide
19831      * @method autoSize
19832      */
19833 });
19834
19835 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19836     
19837     header : {
19838         tag: 'div',
19839         cls: 'modal-header',
19840         cn: [
19841             {
19842                 tag: 'h4',
19843                 cls: 'modal-title'
19844             }
19845         ]
19846     },
19847     
19848     body : {
19849         tag: 'div',
19850         cls: 'modal-body',
19851         cn: [
19852             {
19853                 tag: 'ul',
19854                 cls: 'list-group'
19855             }
19856         ]
19857     },
19858     
19859     listItemRadio : {
19860         tag: 'li',
19861         cls: 'list-group-item',
19862         cn: [
19863             {
19864                 tag: 'span',
19865                 cls: 'roo-combobox-list-group-item-value'
19866             },
19867             {
19868                 tag: 'div',
19869                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19870                 cn: [
19871                     {
19872                         tag: 'input',
19873                         type: 'radio'
19874                     },
19875                     {
19876                         tag: 'label'
19877                     }
19878                 ]
19879             }
19880         ]
19881     },
19882     
19883     listItemCheckbox : {
19884         tag: 'li',
19885         cls: 'list-group-item',
19886         cn: [
19887             {
19888                 tag: 'span',
19889                 cls: 'roo-combobox-list-group-item-value'
19890             },
19891             {
19892                 tag: 'div',
19893                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19894                 cn: [
19895                     {
19896                         tag: 'input',
19897                         type: 'checkbox'
19898                     },
19899                     {
19900                         tag: 'label'
19901                     }
19902                 ]
19903             }
19904         ]
19905     },
19906     
19907     emptyResult : {
19908         tag: 'div',
19909         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19910     },
19911     
19912     footer : {
19913         tag: 'div',
19914         cls: 'modal-footer',
19915         cn: [
19916             {
19917                 tag: 'div',
19918                 cls: 'row',
19919                 cn: [
19920                     {
19921                         tag: 'div',
19922                         cls: 'col-xs-6 text-left',
19923                         cn: {
19924                             tag: 'button',
19925                             cls: 'btn btn-danger roo-touch-view-cancel',
19926                             html: 'Cancel'
19927                         }
19928                     },
19929                     {
19930                         tag: 'div',
19931                         cls: 'col-xs-6 text-right',
19932                         cn: {
19933                             tag: 'button',
19934                             cls: 'btn btn-success roo-touch-view-ok',
19935                             html: 'OK'
19936                         }
19937                     }
19938                 ]
19939             }
19940         ]
19941         
19942     }
19943 });
19944
19945 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19946     
19947     touchViewTemplate : {
19948         tag: 'div',
19949         cls: 'modal fade roo-combobox-touch-view',
19950         cn: [
19951             {
19952                 tag: 'div',
19953                 cls: 'modal-dialog',
19954                 style : 'position:fixed', // we have to fix position....
19955                 cn: [
19956                     {
19957                         tag: 'div',
19958                         cls: 'modal-content',
19959                         cn: [
19960                             Roo.bootstrap.form.ComboBox.header,
19961                             Roo.bootstrap.form.ComboBox.body,
19962                             Roo.bootstrap.form.ComboBox.footer
19963                         ]
19964                     }
19965                 ]
19966             }
19967         ]
19968     }
19969 });/*
19970  * Based on:
19971  * Ext JS Library 1.1.1
19972  * Copyright(c) 2006-2007, Ext JS, LLC.
19973  *
19974  * Originally Released Under LGPL - original licence link has changed is not relivant.
19975  *
19976  * Fork - LGPL
19977  * <script type="text/javascript">
19978  */
19979
19980 /**
19981  * @class Roo.View
19982  * @extends Roo.util.Observable
19983  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19984  * This class also supports single and multi selection modes. <br>
19985  * Create a data model bound view:
19986  <pre><code>
19987  var store = new Roo.data.Store(...);
19988
19989  var view = new Roo.View({
19990     el : "my-element",
19991     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19992  
19993     singleSelect: true,
19994     selectedClass: "ydataview-selected",
19995     store: store
19996  });
19997
19998  // listen for node click?
19999  view.on("click", function(vw, index, node, e){
20000  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
20001  });
20002
20003  // load XML data
20004  dataModel.load("foobar.xml");
20005  </code></pre>
20006  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20007  * <br><br>
20008  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20009  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20010  * 
20011  * Note: old style constructor is still suported (container, template, config)
20012  * 
20013  * @constructor
20014  * Create a new View
20015  * @param {Object} config The config object
20016  * 
20017  */
20018 Roo.View = function(config, depreciated_tpl, depreciated_config){
20019     
20020     this.parent = false;
20021     
20022     if (typeof(depreciated_tpl) == 'undefined') {
20023         // new way.. - universal constructor.
20024         Roo.apply(this, config);
20025         this.el  = Roo.get(this.el);
20026     } else {
20027         // old format..
20028         this.el  = Roo.get(config);
20029         this.tpl = depreciated_tpl;
20030         Roo.apply(this, depreciated_config);
20031     }
20032     this.wrapEl  = this.el.wrap().wrap();
20033     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20034     
20035     
20036     if(typeof(this.tpl) == "string"){
20037         this.tpl = new Roo.Template(this.tpl);
20038     } else {
20039         // support xtype ctors..
20040         this.tpl = new Roo.factory(this.tpl, Roo);
20041     }
20042     
20043     
20044     this.tpl.compile();
20045     
20046     /** @private */
20047     this.addEvents({
20048         /**
20049          * @event beforeclick
20050          * Fires before a click is processed. Returns false to cancel the default action.
20051          * @param {Roo.View} this
20052          * @param {Number} index The index of the target node
20053          * @param {HTMLElement} node The target node
20054          * @param {Roo.EventObject} e The raw event object
20055          */
20056             "beforeclick" : true,
20057         /**
20058          * @event click
20059          * Fires when a template node is clicked.
20060          * @param {Roo.View} this
20061          * @param {Number} index The index of the target node
20062          * @param {HTMLElement} node The target node
20063          * @param {Roo.EventObject} e The raw event object
20064          */
20065             "click" : true,
20066         /**
20067          * @event dblclick
20068          * Fires when a template node is double clicked.
20069          * @param {Roo.View} this
20070          * @param {Number} index The index of the target node
20071          * @param {HTMLElement} node The target node
20072          * @param {Roo.EventObject} e The raw event object
20073          */
20074             "dblclick" : true,
20075         /**
20076          * @event contextmenu
20077          * Fires when a template node is right clicked.
20078          * @param {Roo.View} this
20079          * @param {Number} index The index of the target node
20080          * @param {HTMLElement} node The target node
20081          * @param {Roo.EventObject} e The raw event object
20082          */
20083             "contextmenu" : true,
20084         /**
20085          * @event selectionchange
20086          * Fires when the selected nodes change.
20087          * @param {Roo.View} this
20088          * @param {Array} selections Array of the selected nodes
20089          */
20090             "selectionchange" : true,
20091     
20092         /**
20093          * @event beforeselect
20094          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20095          * @param {Roo.View} this
20096          * @param {HTMLElement} node The node to be selected
20097          * @param {Array} selections Array of currently selected nodes
20098          */
20099             "beforeselect" : true,
20100         /**
20101          * @event preparedata
20102          * Fires on every row to render, to allow you to change the data.
20103          * @param {Roo.View} this
20104          * @param {Object} data to be rendered (change this)
20105          */
20106           "preparedata" : true
20107           
20108           
20109         });
20110
20111
20112
20113     this.el.on({
20114         "click": this.onClick,
20115         "dblclick": this.onDblClick,
20116         "contextmenu": this.onContextMenu,
20117         scope:this
20118     });
20119
20120     this.selections = [];
20121     this.nodes = [];
20122     this.cmp = new Roo.CompositeElementLite([]);
20123     if(this.store){
20124         this.store = Roo.factory(this.store, Roo.data);
20125         this.setStore(this.store, true);
20126     }
20127     
20128     if ( this.footer && this.footer.xtype) {
20129            
20130          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20131         
20132         this.footer.dataSource = this.store;
20133         this.footer.container = fctr;
20134         this.footer = Roo.factory(this.footer, Roo);
20135         fctr.insertFirst(this.el);
20136         
20137         // this is a bit insane - as the paging toolbar seems to detach the el..
20138 //        dom.parentNode.parentNode.parentNode
20139          // they get detached?
20140     }
20141     
20142     
20143     Roo.View.superclass.constructor.call(this);
20144     
20145     
20146 };
20147
20148 Roo.extend(Roo.View, Roo.util.Observable, {
20149     
20150      /**
20151      * @cfg {Roo.data.Store} store Data store to load data from.
20152      */
20153     store : false,
20154     
20155     /**
20156      * @cfg {String|Roo.Element} el The container element.
20157      */
20158     el : '',
20159     
20160     /**
20161      * @cfg {String|Roo.Template} tpl The template used by this View 
20162      */
20163     tpl : false,
20164     /**
20165      * @cfg {String} dataName the named area of the template to use as the data area
20166      *                          Works with domtemplates roo-name="name"
20167      */
20168     dataName: false,
20169     /**
20170      * @cfg {String} selectedClass The css class to add to selected nodes
20171      */
20172     selectedClass : "x-view-selected",
20173      /**
20174      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20175      */
20176     emptyText : "",
20177     
20178     /**
20179      * @cfg {String} text to display on mask (default Loading)
20180      */
20181     mask : false,
20182     /**
20183      * @cfg {Boolean} multiSelect Allow multiple selection
20184      */
20185     multiSelect : false,
20186     /**
20187      * @cfg {Boolean} singleSelect Allow single selection
20188      */
20189     singleSelect:  false,
20190     
20191     /**
20192      * @cfg {Boolean} toggleSelect - selecting 
20193      */
20194     toggleSelect : false,
20195     
20196     /**
20197      * @cfg {Boolean} tickable - selecting 
20198      */
20199     tickable : false,
20200     
20201     /**
20202      * Returns the element this view is bound to.
20203      * @return {Roo.Element}
20204      */
20205     getEl : function(){
20206         return this.wrapEl;
20207     },
20208     
20209     
20210
20211     /**
20212      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20213      */
20214     refresh : function(){
20215         //Roo.log('refresh');
20216         var t = this.tpl;
20217         
20218         // if we are using something like 'domtemplate', then
20219         // the what gets used is:
20220         // t.applySubtemplate(NAME, data, wrapping data..)
20221         // the outer template then get' applied with
20222         //     the store 'extra data'
20223         // and the body get's added to the
20224         //      roo-name="data" node?
20225         //      <span class='roo-tpl-{name}'></span> ?????
20226         
20227         
20228         
20229         this.clearSelections();
20230         this.el.update("");
20231         var html = [];
20232         var records = this.store.getRange();
20233         if(records.length < 1) {
20234             
20235             // is this valid??  = should it render a template??
20236             
20237             this.el.update(this.emptyText);
20238             return;
20239         }
20240         var el = this.el;
20241         if (this.dataName) {
20242             this.el.update(t.apply(this.store.meta)); //????
20243             el = this.el.child('.roo-tpl-' + this.dataName);
20244         }
20245         
20246         for(var i = 0, len = records.length; i < len; i++){
20247             var data = this.prepareData(records[i].data, i, records[i]);
20248             this.fireEvent("preparedata", this, data, i, records[i]);
20249             
20250             var d = Roo.apply({}, data);
20251             
20252             if(this.tickable){
20253                 Roo.apply(d, {'roo-id' : Roo.id()});
20254                 
20255                 var _this = this;
20256             
20257                 Roo.each(this.parent.item, function(item){
20258                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20259                         return;
20260                     }
20261                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20262                 });
20263             }
20264             
20265             html[html.length] = Roo.util.Format.trim(
20266                 this.dataName ?
20267                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20268                     t.apply(d)
20269             );
20270         }
20271         
20272         
20273         
20274         el.update(html.join(""));
20275         this.nodes = el.dom.childNodes;
20276         this.updateIndexes(0);
20277     },
20278     
20279
20280     /**
20281      * Function to override to reformat the data that is sent to
20282      * the template for each node.
20283      * DEPRICATED - use the preparedata event handler.
20284      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20285      * a JSON object for an UpdateManager bound view).
20286      */
20287     prepareData : function(data, index, record)
20288     {
20289         this.fireEvent("preparedata", this, data, index, record);
20290         return data;
20291     },
20292
20293     onUpdate : function(ds, record){
20294         // Roo.log('on update');   
20295         this.clearSelections();
20296         var index = this.store.indexOf(record);
20297         var n = this.nodes[index];
20298         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20299         n.parentNode.removeChild(n);
20300         this.updateIndexes(index, index);
20301     },
20302
20303     
20304     
20305 // --------- FIXME     
20306     onAdd : function(ds, records, index)
20307     {
20308         //Roo.log(['on Add', ds, records, index] );        
20309         this.clearSelections();
20310         if(this.nodes.length == 0){
20311             this.refresh();
20312             return;
20313         }
20314         var n = this.nodes[index];
20315         for(var i = 0, len = records.length; i < len; i++){
20316             var d = this.prepareData(records[i].data, i, records[i]);
20317             if(n){
20318                 this.tpl.insertBefore(n, d);
20319             }else{
20320                 
20321                 this.tpl.append(this.el, d);
20322             }
20323         }
20324         this.updateIndexes(index);
20325     },
20326
20327     onRemove : function(ds, record, index){
20328        // Roo.log('onRemove');
20329         this.clearSelections();
20330         var el = this.dataName  ?
20331             this.el.child('.roo-tpl-' + this.dataName) :
20332             this.el; 
20333         
20334         el.dom.removeChild(this.nodes[index]);
20335         this.updateIndexes(index);
20336     },
20337
20338     /**
20339      * Refresh an individual node.
20340      * @param {Number} index
20341      */
20342     refreshNode : function(index){
20343         this.onUpdate(this.store, this.store.getAt(index));
20344     },
20345
20346     updateIndexes : function(startIndex, endIndex){
20347         var ns = this.nodes;
20348         startIndex = startIndex || 0;
20349         endIndex = endIndex || ns.length - 1;
20350         for(var i = startIndex; i <= endIndex; i++){
20351             ns[i].nodeIndex = i;
20352         }
20353     },
20354
20355     /**
20356      * Changes the data store this view uses and refresh the view.
20357      * @param {Store} store
20358      */
20359     setStore : function(store, initial){
20360         if(!initial && this.store){
20361             this.store.un("datachanged", this.refresh);
20362             this.store.un("add", this.onAdd);
20363             this.store.un("remove", this.onRemove);
20364             this.store.un("update", this.onUpdate);
20365             this.store.un("clear", this.refresh);
20366             this.store.un("beforeload", this.onBeforeLoad);
20367             this.store.un("load", this.onLoad);
20368             this.store.un("loadexception", this.onLoad);
20369         }
20370         if(store){
20371           
20372             store.on("datachanged", this.refresh, this);
20373             store.on("add", this.onAdd, this);
20374             store.on("remove", this.onRemove, this);
20375             store.on("update", this.onUpdate, this);
20376             store.on("clear", this.refresh, this);
20377             store.on("beforeload", this.onBeforeLoad, this);
20378             store.on("load", this.onLoad, this);
20379             store.on("loadexception", this.onLoad, this);
20380         }
20381         
20382         if(store){
20383             this.refresh();
20384         }
20385     },
20386     /**
20387      * onbeforeLoad - masks the loading area.
20388      *
20389      */
20390     onBeforeLoad : function(store,opts)
20391     {
20392          //Roo.log('onBeforeLoad');   
20393         if (!opts.add) {
20394             this.el.update("");
20395         }
20396         this.el.mask(this.mask ? this.mask : "Loading" ); 
20397     },
20398     onLoad : function ()
20399     {
20400         this.el.unmask();
20401     },
20402     
20403
20404     /**
20405      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20406      * @param {HTMLElement} node
20407      * @return {HTMLElement} The template node
20408      */
20409     findItemFromChild : function(node){
20410         var el = this.dataName  ?
20411             this.el.child('.roo-tpl-' + this.dataName,true) :
20412             this.el.dom; 
20413         
20414         if(!node || node.parentNode == el){
20415                     return node;
20416             }
20417             var p = node.parentNode;
20418             while(p && p != el){
20419             if(p.parentNode == el){
20420                 return p;
20421             }
20422             p = p.parentNode;
20423         }
20424             return null;
20425     },
20426
20427     /** @ignore */
20428     onClick : function(e){
20429         var item = this.findItemFromChild(e.getTarget());
20430         if(item){
20431             var index = this.indexOf(item);
20432             if(this.onItemClick(item, index, e) !== false){
20433                 this.fireEvent("click", this, index, item, e);
20434             }
20435         }else{
20436             this.clearSelections();
20437         }
20438     },
20439
20440     /** @ignore */
20441     onContextMenu : function(e){
20442         var item = this.findItemFromChild(e.getTarget());
20443         if(item){
20444             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20445         }
20446     },
20447
20448     /** @ignore */
20449     onDblClick : function(e){
20450         var item = this.findItemFromChild(e.getTarget());
20451         if(item){
20452             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20453         }
20454     },
20455
20456     onItemClick : function(item, index, e)
20457     {
20458         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20459             return false;
20460         }
20461         if (this.toggleSelect) {
20462             var m = this.isSelected(item) ? 'unselect' : 'select';
20463             //Roo.log(m);
20464             var _t = this;
20465             _t[m](item, true, false);
20466             return true;
20467         }
20468         if(this.multiSelect || this.singleSelect){
20469             if(this.multiSelect && e.shiftKey && this.lastSelection){
20470                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20471             }else{
20472                 this.select(item, this.multiSelect && e.ctrlKey);
20473                 this.lastSelection = item;
20474             }
20475             
20476             if(!this.tickable){
20477                 e.preventDefault();
20478             }
20479             
20480         }
20481         return true;
20482     },
20483
20484     /**
20485      * Get the number of selected nodes.
20486      * @return {Number}
20487      */
20488     getSelectionCount : function(){
20489         return this.selections.length;
20490     },
20491
20492     /**
20493      * Get the currently selected nodes.
20494      * @return {Array} An array of HTMLElements
20495      */
20496     getSelectedNodes : function(){
20497         return this.selections;
20498     },
20499
20500     /**
20501      * Get the indexes of the selected nodes.
20502      * @return {Array}
20503      */
20504     getSelectedIndexes : function(){
20505         var indexes = [], s = this.selections;
20506         for(var i = 0, len = s.length; i < len; i++){
20507             indexes.push(s[i].nodeIndex);
20508         }
20509         return indexes;
20510     },
20511
20512     /**
20513      * Clear all selections
20514      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20515      */
20516     clearSelections : function(suppressEvent){
20517         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20518             this.cmp.elements = this.selections;
20519             this.cmp.removeClass(this.selectedClass);
20520             this.selections = [];
20521             if(!suppressEvent){
20522                 this.fireEvent("selectionchange", this, this.selections);
20523             }
20524         }
20525     },
20526
20527     /**
20528      * Returns true if the passed node is selected
20529      * @param {HTMLElement/Number} node The node or node index
20530      * @return {Boolean}
20531      */
20532     isSelected : function(node){
20533         var s = this.selections;
20534         if(s.length < 1){
20535             return false;
20536         }
20537         node = this.getNode(node);
20538         return s.indexOf(node) !== -1;
20539     },
20540
20541     /**
20542      * Selects nodes.
20543      * @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
20544      * @param {Boolean} keepExisting (optional) true to keep existing selections
20545      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20546      */
20547     select : function(nodeInfo, keepExisting, suppressEvent){
20548         if(nodeInfo instanceof Array){
20549             if(!keepExisting){
20550                 this.clearSelections(true);
20551             }
20552             for(var i = 0, len = nodeInfo.length; i < len; i++){
20553                 this.select(nodeInfo[i], true, true);
20554             }
20555             return;
20556         } 
20557         var node = this.getNode(nodeInfo);
20558         if(!node || this.isSelected(node)){
20559             return; // already selected.
20560         }
20561         if(!keepExisting){
20562             this.clearSelections(true);
20563         }
20564         
20565         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20566             Roo.fly(node).addClass(this.selectedClass);
20567             this.selections.push(node);
20568             if(!suppressEvent){
20569                 this.fireEvent("selectionchange", this, this.selections);
20570             }
20571         }
20572         
20573         
20574     },
20575       /**
20576      * Unselects nodes.
20577      * @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
20578      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20579      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20580      */
20581     unselect : function(nodeInfo, keepExisting, suppressEvent)
20582     {
20583         if(nodeInfo instanceof Array){
20584             Roo.each(this.selections, function(s) {
20585                 this.unselect(s, nodeInfo);
20586             }, this);
20587             return;
20588         }
20589         var node = this.getNode(nodeInfo);
20590         if(!node || !this.isSelected(node)){
20591             //Roo.log("not selected");
20592             return; // not selected.
20593         }
20594         // fireevent???
20595         var ns = [];
20596         Roo.each(this.selections, function(s) {
20597             if (s == node ) {
20598                 Roo.fly(node).removeClass(this.selectedClass);
20599
20600                 return;
20601             }
20602             ns.push(s);
20603         },this);
20604         
20605         this.selections= ns;
20606         this.fireEvent("selectionchange", this, this.selections);
20607     },
20608
20609     /**
20610      * Gets a template node.
20611      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20612      * @return {HTMLElement} The node or null if it wasn't found
20613      */
20614     getNode : function(nodeInfo){
20615         if(typeof nodeInfo == "string"){
20616             return document.getElementById(nodeInfo);
20617         }else if(typeof nodeInfo == "number"){
20618             return this.nodes[nodeInfo];
20619         }
20620         return nodeInfo;
20621     },
20622
20623     /**
20624      * Gets a range template nodes.
20625      * @param {Number} startIndex
20626      * @param {Number} endIndex
20627      * @return {Array} An array of nodes
20628      */
20629     getNodes : function(start, end){
20630         var ns = this.nodes;
20631         start = start || 0;
20632         end = typeof end == "undefined" ? ns.length - 1 : end;
20633         var nodes = [];
20634         if(start <= end){
20635             for(var i = start; i <= end; i++){
20636                 nodes.push(ns[i]);
20637             }
20638         } else{
20639             for(var i = start; i >= end; i--){
20640                 nodes.push(ns[i]);
20641             }
20642         }
20643         return nodes;
20644     },
20645
20646     /**
20647      * Finds the index of the passed node
20648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20649      * @return {Number} The index of the node or -1
20650      */
20651     indexOf : function(node){
20652         node = this.getNode(node);
20653         if(typeof node.nodeIndex == "number"){
20654             return node.nodeIndex;
20655         }
20656         var ns = this.nodes;
20657         for(var i = 0, len = ns.length; i < len; i++){
20658             if(ns[i] == node){
20659                 return i;
20660             }
20661         }
20662         return -1;
20663     }
20664 });
20665 /*
20666  * - LGPL
20667  *
20668  * based on jquery fullcalendar
20669  * 
20670  */
20671
20672 Roo.bootstrap = Roo.bootstrap || {};
20673 /**
20674  * @class Roo.bootstrap.Calendar
20675  * @extends Roo.bootstrap.Component
20676  * Bootstrap Calendar class
20677  * @cfg {Boolean} loadMask (true|false) default false
20678  * @cfg {Object} header generate the user specific header of the calendar, default false
20679
20680  * @constructor
20681  * Create a new Container
20682  * @param {Object} config The config object
20683  */
20684
20685
20686
20687 Roo.bootstrap.Calendar = function(config){
20688     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20689      this.addEvents({
20690         /**
20691              * @event select
20692              * Fires when a date is selected
20693              * @param {DatePicker} this
20694              * @param {Date} date The selected date
20695              */
20696         'select': true,
20697         /**
20698              * @event monthchange
20699              * Fires when the displayed month changes 
20700              * @param {DatePicker} this
20701              * @param {Date} date The selected month
20702              */
20703         'monthchange': true,
20704         /**
20705              * @event evententer
20706              * Fires when mouse over an event
20707              * @param {Calendar} this
20708              * @param {event} Event
20709              */
20710         'evententer': true,
20711         /**
20712              * @event eventleave
20713              * Fires when the mouse leaves an
20714              * @param {Calendar} this
20715              * @param {event}
20716              */
20717         'eventleave': true,
20718         /**
20719              * @event eventclick
20720              * Fires when the mouse click an
20721              * @param {Calendar} this
20722              * @param {event}
20723              */
20724         'eventclick': true
20725         
20726     });
20727
20728 };
20729
20730 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20731     
20732           /**
20733      * @cfg {Roo.data.Store} store
20734      * The data source for the calendar
20735      */
20736         store : false,
20737      /**
20738      * @cfg {Number} startDay
20739      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20740      */
20741     startDay : 0,
20742     
20743     loadMask : false,
20744     
20745     header : false,
20746       
20747     getAutoCreate : function(){
20748         
20749         
20750         var fc_button = function(name, corner, style, content ) {
20751             return Roo.apply({},{
20752                 tag : 'span',
20753                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20754                          (corner.length ?
20755                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20756                             ''
20757                         ),
20758                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20759                 unselectable: 'on'
20760             });
20761         };
20762         
20763         var header = {};
20764         
20765         if(!this.header){
20766             header = {
20767                 tag : 'table',
20768                 cls : 'fc-header',
20769                 style : 'width:100%',
20770                 cn : [
20771                     {
20772                         tag: 'tr',
20773                         cn : [
20774                             {
20775                                 tag : 'td',
20776                                 cls : 'fc-header-left',
20777                                 cn : [
20778                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20779                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20780                                     { tag: 'span', cls: 'fc-header-space' },
20781                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20782
20783
20784                                 ]
20785                             },
20786
20787                             {
20788                                 tag : 'td',
20789                                 cls : 'fc-header-center',
20790                                 cn : [
20791                                     {
20792                                         tag: 'span',
20793                                         cls: 'fc-header-title',
20794                                         cn : {
20795                                             tag: 'H2',
20796                                             html : 'month / year'
20797                                         }
20798                                     }
20799
20800                                 ]
20801                             },
20802                             {
20803                                 tag : 'td',
20804                                 cls : 'fc-header-right',
20805                                 cn : [
20806                               /*      fc_button('month', 'left', '', 'month' ),
20807                                     fc_button('week', '', '', 'week' ),
20808                                     fc_button('day', 'right', '', 'day' )
20809                                 */    
20810
20811                                 ]
20812                             }
20813
20814                         ]
20815                     }
20816                 ]
20817             };
20818         }
20819         
20820         header = this.header;
20821         
20822        
20823         var cal_heads = function() {
20824             var ret = [];
20825             // fixme - handle this.
20826             
20827             for (var i =0; i < Date.dayNames.length; i++) {
20828                 var d = Date.dayNames[i];
20829                 ret.push({
20830                     tag: 'th',
20831                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20832                     html : d.substring(0,3)
20833                 });
20834                 
20835             }
20836             ret[0].cls += ' fc-first';
20837             ret[6].cls += ' fc-last';
20838             return ret;
20839         };
20840         var cal_cell = function(n) {
20841             return  {
20842                 tag: 'td',
20843                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20844                 cn : [
20845                     {
20846                         cn : [
20847                             {
20848                                 cls: 'fc-day-number',
20849                                 html: 'D'
20850                             },
20851                             {
20852                                 cls: 'fc-day-content',
20853                              
20854                                 cn : [
20855                                      {
20856                                         style: 'position: relative;' // height: 17px;
20857                                     }
20858                                 ]
20859                             }
20860                             
20861                             
20862                         ]
20863                     }
20864                 ]
20865                 
20866             }
20867         };
20868         var cal_rows = function() {
20869             
20870             var ret = [];
20871             for (var r = 0; r < 6; r++) {
20872                 var row= {
20873                     tag : 'tr',
20874                     cls : 'fc-week',
20875                     cn : []
20876                 };
20877                 
20878                 for (var i =0; i < Date.dayNames.length; i++) {
20879                     var d = Date.dayNames[i];
20880                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20881
20882                 }
20883                 row.cn[0].cls+=' fc-first';
20884                 row.cn[0].cn[0].style = 'min-height:90px';
20885                 row.cn[6].cls+=' fc-last';
20886                 ret.push(row);
20887                 
20888             }
20889             ret[0].cls += ' fc-first';
20890             ret[4].cls += ' fc-prev-last';
20891             ret[5].cls += ' fc-last';
20892             return ret;
20893             
20894         };
20895         
20896         var cal_table = {
20897             tag: 'table',
20898             cls: 'fc-border-separate',
20899             style : 'width:100%',
20900             cellspacing  : 0,
20901             cn : [
20902                 { 
20903                     tag: 'thead',
20904                     cn : [
20905                         { 
20906                             tag: 'tr',
20907                             cls : 'fc-first fc-last',
20908                             cn : cal_heads()
20909                         }
20910                     ]
20911                 },
20912                 { 
20913                     tag: 'tbody',
20914                     cn : cal_rows()
20915                 }
20916                   
20917             ]
20918         };
20919          
20920          var cfg = {
20921             cls : 'fc fc-ltr',
20922             cn : [
20923                 header,
20924                 {
20925                     cls : 'fc-content',
20926                     style : "position: relative;",
20927                     cn : [
20928                         {
20929                             cls : 'fc-view fc-view-month fc-grid',
20930                             style : 'position: relative',
20931                             unselectable : 'on',
20932                             cn : [
20933                                 {
20934                                     cls : 'fc-event-container',
20935                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20936                                 },
20937                                 cal_table
20938                             ]
20939                         }
20940                     ]
20941     
20942                 }
20943            ] 
20944             
20945         };
20946         
20947          
20948         
20949         return cfg;
20950     },
20951     
20952     
20953     initEvents : function()
20954     {
20955         if(!this.store){
20956             throw "can not find store for calendar";
20957         }
20958         
20959         var mark = {
20960             tag: "div",
20961             cls:"x-dlg-mask",
20962             style: "text-align:center",
20963             cn: [
20964                 {
20965                     tag: "div",
20966                     style: "background-color:white;width:50%;margin:250 auto",
20967                     cn: [
20968                         {
20969                             tag: "img",
20970                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20971                         },
20972                         {
20973                             tag: "span",
20974                             html: "Loading"
20975                         }
20976                         
20977                     ]
20978                 }
20979             ]
20980         };
20981         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20982         
20983         var size = this.el.select('.fc-content', true).first().getSize();
20984         this.maskEl.setSize(size.width, size.height);
20985         this.maskEl.enableDisplayMode("block");
20986         if(!this.loadMask){
20987             this.maskEl.hide();
20988         }
20989         
20990         this.store = Roo.factory(this.store, Roo.data);
20991         this.store.on('load', this.onLoad, this);
20992         this.store.on('beforeload', this.onBeforeLoad, this);
20993         
20994         this.resize();
20995         
20996         this.cells = this.el.select('.fc-day',true);
20997         //Roo.log(this.cells);
20998         this.textNodes = this.el.query('.fc-day-number');
20999         this.cells.addClassOnOver('fc-state-hover');
21000         
21001         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
21002         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
21003         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
21004         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21005         
21006         this.on('monthchange', this.onMonthChange, this);
21007         
21008         this.update(new Date().clearTime());
21009     },
21010     
21011     resize : function() {
21012         var sz  = this.el.getSize();
21013         
21014         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21015         this.el.select('.fc-day-content div',true).setHeight(34);
21016     },
21017     
21018     
21019     // private
21020     showPrevMonth : function(e){
21021         this.update(this.activeDate.add("mo", -1));
21022     },
21023     showToday : function(e){
21024         this.update(new Date().clearTime());
21025     },
21026     // private
21027     showNextMonth : function(e){
21028         this.update(this.activeDate.add("mo", 1));
21029     },
21030
21031     // private
21032     showPrevYear : function(){
21033         this.update(this.activeDate.add("y", -1));
21034     },
21035
21036     // private
21037     showNextYear : function(){
21038         this.update(this.activeDate.add("y", 1));
21039     },
21040
21041     
21042    // private
21043     update : function(date)
21044     {
21045         var vd = this.activeDate;
21046         this.activeDate = date;
21047 //        if(vd && this.el){
21048 //            var t = date.getTime();
21049 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21050 //                Roo.log('using add remove');
21051 //                
21052 //                this.fireEvent('monthchange', this, date);
21053 //                
21054 //                this.cells.removeClass("fc-state-highlight");
21055 //                this.cells.each(function(c){
21056 //                   if(c.dateValue == t){
21057 //                       c.addClass("fc-state-highlight");
21058 //                       setTimeout(function(){
21059 //                            try{c.dom.firstChild.focus();}catch(e){}
21060 //                       }, 50);
21061 //                       return false;
21062 //                   }
21063 //                   return true;
21064 //                });
21065 //                return;
21066 //            }
21067 //        }
21068         
21069         var days = date.getDaysInMonth();
21070         
21071         var firstOfMonth = date.getFirstDateOfMonth();
21072         var startingPos = firstOfMonth.getDay()-this.startDay;
21073         
21074         if(startingPos < this.startDay){
21075             startingPos += 7;
21076         }
21077         
21078         var pm = date.add(Date.MONTH, -1);
21079         var prevStart = pm.getDaysInMonth()-startingPos;
21080 //        
21081         this.cells = this.el.select('.fc-day',true);
21082         this.textNodes = this.el.query('.fc-day-number');
21083         this.cells.addClassOnOver('fc-state-hover');
21084         
21085         var cells = this.cells.elements;
21086         var textEls = this.textNodes;
21087         
21088         Roo.each(cells, function(cell){
21089             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21090         });
21091         
21092         days += startingPos;
21093
21094         // convert everything to numbers so it's fast
21095         var day = 86400000;
21096         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21097         //Roo.log(d);
21098         //Roo.log(pm);
21099         //Roo.log(prevStart);
21100         
21101         var today = new Date().clearTime().getTime();
21102         var sel = date.clearTime().getTime();
21103         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21104         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21105         var ddMatch = this.disabledDatesRE;
21106         var ddText = this.disabledDatesText;
21107         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21108         var ddaysText = this.disabledDaysText;
21109         var format = this.format;
21110         
21111         var setCellClass = function(cal, cell){
21112             cell.row = 0;
21113             cell.events = [];
21114             cell.more = [];
21115             //Roo.log('set Cell Class');
21116             cell.title = "";
21117             var t = d.getTime();
21118             
21119             //Roo.log(d);
21120             
21121             cell.dateValue = t;
21122             if(t == today){
21123                 cell.className += " fc-today";
21124                 cell.className += " fc-state-highlight";
21125                 cell.title = cal.todayText;
21126             }
21127             if(t == sel){
21128                 // disable highlight in other month..
21129                 //cell.className += " fc-state-highlight";
21130                 
21131             }
21132             // disabling
21133             if(t < min) {
21134                 cell.className = " fc-state-disabled";
21135                 cell.title = cal.minText;
21136                 return;
21137             }
21138             if(t > max) {
21139                 cell.className = " fc-state-disabled";
21140                 cell.title = cal.maxText;
21141                 return;
21142             }
21143             if(ddays){
21144                 if(ddays.indexOf(d.getDay()) != -1){
21145                     cell.title = ddaysText;
21146                     cell.className = " fc-state-disabled";
21147                 }
21148             }
21149             if(ddMatch && format){
21150                 var fvalue = d.dateFormat(format);
21151                 if(ddMatch.test(fvalue)){
21152                     cell.title = ddText.replace("%0", fvalue);
21153                     cell.className = " fc-state-disabled";
21154                 }
21155             }
21156             
21157             if (!cell.initialClassName) {
21158                 cell.initialClassName = cell.dom.className;
21159             }
21160             
21161             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21162         };
21163
21164         var i = 0;
21165         
21166         for(; i < startingPos; i++) {
21167             textEls[i].innerHTML = (++prevStart);
21168             d.setDate(d.getDate()+1);
21169             
21170             cells[i].className = "fc-past fc-other-month";
21171             setCellClass(this, cells[i]);
21172         }
21173         
21174         var intDay = 0;
21175         
21176         for(; i < days; i++){
21177             intDay = i - startingPos + 1;
21178             textEls[i].innerHTML = (intDay);
21179             d.setDate(d.getDate()+1);
21180             
21181             cells[i].className = ''; // "x-date-active";
21182             setCellClass(this, cells[i]);
21183         }
21184         var extraDays = 0;
21185         
21186         for(; i < 42; i++) {
21187             textEls[i].innerHTML = (++extraDays);
21188             d.setDate(d.getDate()+1);
21189             
21190             cells[i].className = "fc-future fc-other-month";
21191             setCellClass(this, cells[i]);
21192         }
21193         
21194         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21195         
21196         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21197         
21198         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21199         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21200         
21201         if(totalRows != 6){
21202             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21203             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21204         }
21205         
21206         this.fireEvent('monthchange', this, date);
21207         
21208         
21209         /*
21210         if(!this.internalRender){
21211             var main = this.el.dom.firstChild;
21212             var w = main.offsetWidth;
21213             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21214             Roo.fly(main).setWidth(w);
21215             this.internalRender = true;
21216             // opera does not respect the auto grow header center column
21217             // then, after it gets a width opera refuses to recalculate
21218             // without a second pass
21219             if(Roo.isOpera && !this.secondPass){
21220                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21221                 this.secondPass = true;
21222                 this.update.defer(10, this, [date]);
21223             }
21224         }
21225         */
21226         
21227     },
21228     
21229     findCell : function(dt) {
21230         dt = dt.clearTime().getTime();
21231         var ret = false;
21232         this.cells.each(function(c){
21233             //Roo.log("check " +c.dateValue + '?=' + dt);
21234             if(c.dateValue == dt){
21235                 ret = c;
21236                 return false;
21237             }
21238             return true;
21239         });
21240         
21241         return ret;
21242     },
21243     
21244     findCells : function(ev) {
21245         var s = ev.start.clone().clearTime().getTime();
21246        // Roo.log(s);
21247         var e= ev.end.clone().clearTime().getTime();
21248        // Roo.log(e);
21249         var ret = [];
21250         this.cells.each(function(c){
21251              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21252             
21253             if(c.dateValue > e){
21254                 return ;
21255             }
21256             if(c.dateValue < s){
21257                 return ;
21258             }
21259             ret.push(c);
21260         });
21261         
21262         return ret;    
21263     },
21264     
21265 //    findBestRow: function(cells)
21266 //    {
21267 //        var ret = 0;
21268 //        
21269 //        for (var i =0 ; i < cells.length;i++) {
21270 //            ret  = Math.max(cells[i].rows || 0,ret);
21271 //        }
21272 //        return ret;
21273 //        
21274 //    },
21275     
21276     
21277     addItem : function(ev)
21278     {
21279         // look for vertical location slot in
21280         var cells = this.findCells(ev);
21281         
21282 //        ev.row = this.findBestRow(cells);
21283         
21284         // work out the location.
21285         
21286         var crow = false;
21287         var rows = [];
21288         for(var i =0; i < cells.length; i++) {
21289             
21290             cells[i].row = cells[0].row;
21291             
21292             if(i == 0){
21293                 cells[i].row = cells[i].row + 1;
21294             }
21295             
21296             if (!crow) {
21297                 crow = {
21298                     start : cells[i],
21299                     end :  cells[i]
21300                 };
21301                 continue;
21302             }
21303             if (crow.start.getY() == cells[i].getY()) {
21304                 // on same row.
21305                 crow.end = cells[i];
21306                 continue;
21307             }
21308             // different row.
21309             rows.push(crow);
21310             crow = {
21311                 start: cells[i],
21312                 end : cells[i]
21313             };
21314             
21315         }
21316         
21317         rows.push(crow);
21318         ev.els = [];
21319         ev.rows = rows;
21320         ev.cells = cells;
21321         
21322         cells[0].events.push(ev);
21323         
21324         this.calevents.push(ev);
21325     },
21326     
21327     clearEvents: function() {
21328         
21329         if(!this.calevents){
21330             return;
21331         }
21332         
21333         Roo.each(this.cells.elements, function(c){
21334             c.row = 0;
21335             c.events = [];
21336             c.more = [];
21337         });
21338         
21339         Roo.each(this.calevents, function(e) {
21340             Roo.each(e.els, function(el) {
21341                 el.un('mouseenter' ,this.onEventEnter, this);
21342                 el.un('mouseleave' ,this.onEventLeave, this);
21343                 el.remove();
21344             },this);
21345         },this);
21346         
21347         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21348             e.remove();
21349         });
21350         
21351     },
21352     
21353     renderEvents: function()
21354     {   
21355         var _this = this;
21356         
21357         this.cells.each(function(c) {
21358             
21359             if(c.row < 5){
21360                 return;
21361             }
21362             
21363             var ev = c.events;
21364             
21365             var r = 4;
21366             if(c.row != c.events.length){
21367                 r = 4 - (4 - (c.row - c.events.length));
21368             }
21369             
21370             c.events = ev.slice(0, r);
21371             c.more = ev.slice(r);
21372             
21373             if(c.more.length && c.more.length == 1){
21374                 c.events.push(c.more.pop());
21375             }
21376             
21377             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21378             
21379         });
21380             
21381         this.cells.each(function(c) {
21382             
21383             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21384             
21385             
21386             for (var e = 0; e < c.events.length; e++){
21387                 var ev = c.events[e];
21388                 var rows = ev.rows;
21389                 
21390                 for(var i = 0; i < rows.length; i++) {
21391                 
21392                     // how many rows should it span..
21393
21394                     var  cfg = {
21395                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21396                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21397
21398                         unselectable : "on",
21399                         cn : [
21400                             {
21401                                 cls: 'fc-event-inner',
21402                                 cn : [
21403     //                                {
21404     //                                  tag:'span',
21405     //                                  cls: 'fc-event-time',
21406     //                                  html : cells.length > 1 ? '' : ev.time
21407     //                                },
21408                                     {
21409                                       tag:'span',
21410                                       cls: 'fc-event-title',
21411                                       html : String.format('{0}', ev.title)
21412                                     }
21413
21414
21415                                 ]
21416                             },
21417                             {
21418                                 cls: 'ui-resizable-handle ui-resizable-e',
21419                                 html : '&nbsp;&nbsp;&nbsp'
21420                             }
21421
21422                         ]
21423                     };
21424
21425                     if (i == 0) {
21426                         cfg.cls += ' fc-event-start';
21427                     }
21428                     if ((i+1) == rows.length) {
21429                         cfg.cls += ' fc-event-end';
21430                     }
21431
21432                     var ctr = _this.el.select('.fc-event-container',true).first();
21433                     var cg = ctr.createChild(cfg);
21434
21435                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21436                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21437
21438                     var r = (c.more.length) ? 1 : 0;
21439                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21440                     cg.setWidth(ebox.right - sbox.x -2);
21441
21442                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21443                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21444                     cg.on('click', _this.onEventClick, _this, ev);
21445
21446                     ev.els.push(cg);
21447                     
21448                 }
21449                 
21450             }
21451             
21452             
21453             if(c.more.length){
21454                 var  cfg = {
21455                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21456                     style : 'position: absolute',
21457                     unselectable : "on",
21458                     cn : [
21459                         {
21460                             cls: 'fc-event-inner',
21461                             cn : [
21462                                 {
21463                                   tag:'span',
21464                                   cls: 'fc-event-title',
21465                                   html : 'More'
21466                                 }
21467
21468
21469                             ]
21470                         },
21471                         {
21472                             cls: 'ui-resizable-handle ui-resizable-e',
21473                             html : '&nbsp;&nbsp;&nbsp'
21474                         }
21475
21476                     ]
21477                 };
21478
21479                 var ctr = _this.el.select('.fc-event-container',true).first();
21480                 var cg = ctr.createChild(cfg);
21481
21482                 var sbox = c.select('.fc-day-content',true).first().getBox();
21483                 var ebox = c.select('.fc-day-content',true).first().getBox();
21484                 //Roo.log(cg);
21485                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21486                 cg.setWidth(ebox.right - sbox.x -2);
21487
21488                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21489                 
21490             }
21491             
21492         });
21493         
21494         
21495         
21496     },
21497     
21498     onEventEnter: function (e, el,event,d) {
21499         this.fireEvent('evententer', this, el, event);
21500     },
21501     
21502     onEventLeave: function (e, el,event,d) {
21503         this.fireEvent('eventleave', this, el, event);
21504     },
21505     
21506     onEventClick: function (e, el,event,d) {
21507         this.fireEvent('eventclick', this, el, event);
21508     },
21509     
21510     onMonthChange: function () {
21511         this.store.load();
21512     },
21513     
21514     onMoreEventClick: function(e, el, more)
21515     {
21516         var _this = this;
21517         
21518         this.calpopover.placement = 'right';
21519         this.calpopover.setTitle('More');
21520         
21521         this.calpopover.setContent('');
21522         
21523         var ctr = this.calpopover.el.select('.popover-content', true).first();
21524         
21525         Roo.each(more, function(m){
21526             var cfg = {
21527                 cls : 'fc-event-hori fc-event-draggable',
21528                 html : m.title
21529             };
21530             var cg = ctr.createChild(cfg);
21531             
21532             cg.on('click', _this.onEventClick, _this, m);
21533         });
21534         
21535         this.calpopover.show(el);
21536         
21537         
21538     },
21539     
21540     onLoad: function () 
21541     {   
21542         this.calevents = [];
21543         var cal = this;
21544         
21545         if(this.store.getCount() > 0){
21546             this.store.data.each(function(d){
21547                cal.addItem({
21548                     id : d.data.id,
21549                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21550                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21551                     time : d.data.start_time,
21552                     title : d.data.title,
21553                     description : d.data.description,
21554                     venue : d.data.venue
21555                 });
21556             });
21557         }
21558         
21559         this.renderEvents();
21560         
21561         if(this.calevents.length && this.loadMask){
21562             this.maskEl.hide();
21563         }
21564     },
21565     
21566     onBeforeLoad: function()
21567     {
21568         this.clearEvents();
21569         if(this.loadMask){
21570             this.maskEl.show();
21571         }
21572     }
21573 });
21574
21575  
21576  /*
21577  * - LGPL
21578  *
21579  * element
21580  * 
21581  */
21582
21583 /**
21584  * @class Roo.bootstrap.Popover
21585  * @extends Roo.bootstrap.Component
21586  * @parent none builder
21587  * @children Roo.bootstrap.Component
21588  * Bootstrap Popover class
21589  * @cfg {String} html contents of the popover   (or false to use children..)
21590  * @cfg {String} title of popover (or false to hide)
21591  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21592  * @cfg {String} trigger click || hover (or false to trigger manually)
21593  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21594  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21595  *      - if false and it has a 'parent' then it will be automatically added to that element
21596  *      - if string - Roo.get  will be called 
21597  * @cfg {Number} delay - delay before showing
21598  
21599  * @constructor
21600  * Create a new Popover
21601  * @param {Object} config The config object
21602  */
21603
21604 Roo.bootstrap.Popover = function(config){
21605     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21606     
21607     this.addEvents({
21608         // raw events
21609          /**
21610          * @event show
21611          * After the popover show
21612          * 
21613          * @param {Roo.bootstrap.Popover} this
21614          */
21615         "show" : true,
21616         /**
21617          * @event hide
21618          * After the popover hide
21619          * 
21620          * @param {Roo.bootstrap.Popover} this
21621          */
21622         "hide" : true
21623     });
21624 };
21625
21626 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21627     
21628     title: false,
21629     html: false,
21630     
21631     placement : 'right',
21632     trigger : 'hover', // hover
21633     modal : false,
21634     delay : 0,
21635     
21636     over: false,
21637     
21638     can_build_overlaid : false,
21639     
21640     maskEl : false, // the mask element
21641     headerEl : false,
21642     contentEl : false,
21643     alignEl : false, // when show is called with an element - this get's stored.
21644     
21645     getChildContainer : function()
21646     {
21647         return this.contentEl;
21648         
21649     },
21650     getPopoverHeader : function()
21651     {
21652         this.title = true; // flag not to hide it..
21653         this.headerEl.addClass('p-0');
21654         return this.headerEl
21655     },
21656     
21657     
21658     getAutoCreate : function(){
21659          
21660         var cfg = {
21661            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21662            style: 'display:block',
21663            cn : [
21664                 {
21665                     cls : 'arrow'
21666                 },
21667                 {
21668                     cls : 'popover-inner ',
21669                     cn : [
21670                         {
21671                             tag: 'h3',
21672                             cls: 'popover-title popover-header',
21673                             html : this.title === false ? '' : this.title
21674                         },
21675                         {
21676                             cls : 'popover-content popover-body '  + (this.cls || ''),
21677                             html : this.html || ''
21678                         }
21679                     ]
21680                     
21681                 }
21682            ]
21683         };
21684         
21685         return cfg;
21686     },
21687     /**
21688      * @param {string} the title
21689      */
21690     setTitle: function(str)
21691     {
21692         this.title = str;
21693         if (this.el) {
21694             this.headerEl.dom.innerHTML = str;
21695         }
21696         
21697     },
21698     /**
21699      * @param {string} the body content
21700      */
21701     setContent: function(str)
21702     {
21703         this.html = str;
21704         if (this.contentEl) {
21705             this.contentEl.dom.innerHTML = str;
21706         }
21707         
21708     },
21709     // as it get's added to the bottom of the page.
21710     onRender : function(ct, position)
21711     {
21712         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21713         
21714         
21715         
21716         if(!this.el){
21717             var cfg = Roo.apply({},  this.getAutoCreate());
21718             cfg.id = Roo.id();
21719             
21720             if (this.cls) {
21721                 cfg.cls += ' ' + this.cls;
21722             }
21723             if (this.style) {
21724                 cfg.style = this.style;
21725             }
21726             //Roo.log("adding to ");
21727             this.el = Roo.get(document.body).createChild(cfg, position);
21728 //            Roo.log(this.el);
21729         }
21730         
21731         this.contentEl = this.el.select('.popover-content',true).first();
21732         this.headerEl =  this.el.select('.popover-title',true).first();
21733         
21734         var nitems = [];
21735         if(typeof(this.items) != 'undefined'){
21736             var items = this.items;
21737             delete this.items;
21738
21739             for(var i =0;i < items.length;i++) {
21740                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21741             }
21742         }
21743
21744         this.items = nitems;
21745         
21746         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21747         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21748         
21749         
21750         
21751         this.initEvents();
21752     },
21753     
21754     resizeMask : function()
21755     {
21756         this.maskEl.setSize(
21757             Roo.lib.Dom.getViewWidth(true),
21758             Roo.lib.Dom.getViewHeight(true)
21759         );
21760     },
21761     
21762     initEvents : function()
21763     {
21764         
21765         if (!this.modal) { 
21766             Roo.bootstrap.Popover.register(this);
21767         }
21768          
21769         this.arrowEl = this.el.select('.arrow',true).first();
21770         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21771         this.el.enableDisplayMode('block');
21772         this.el.hide();
21773  
21774         
21775         if (this.over === false && !this.parent()) {
21776             return; 
21777         }
21778         if (this.triggers === false) {
21779             return;
21780         }
21781          
21782         // support parent
21783         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21784         var triggers = this.trigger ? this.trigger.split(' ') : [];
21785         Roo.each(triggers, function(trigger) {
21786         
21787             if (trigger == 'click') {
21788                 on_el.on('click', this.toggle, this);
21789             } else if (trigger != 'manual') {
21790                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21791                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21792       
21793                 on_el.on(eventIn  ,this.enter, this);
21794                 on_el.on(eventOut, this.leave, this);
21795             }
21796         }, this);
21797     },
21798     
21799     
21800     // private
21801     timeout : null,
21802     hoverState : null,
21803     
21804     toggle : function () {
21805         this.hoverState == 'in' ? this.leave() : this.enter();
21806     },
21807     
21808     enter : function () {
21809         
21810         clearTimeout(this.timeout);
21811     
21812         this.hoverState = 'in';
21813     
21814         if (!this.delay || !this.delay.show) {
21815             this.show();
21816             return;
21817         }
21818         var _t = this;
21819         this.timeout = setTimeout(function () {
21820             if (_t.hoverState == 'in') {
21821                 _t.show();
21822             }
21823         }, this.delay.show)
21824     },
21825     
21826     leave : function() {
21827         clearTimeout(this.timeout);
21828     
21829         this.hoverState = 'out';
21830     
21831         if (!this.delay || !this.delay.hide) {
21832             this.hide();
21833             return;
21834         }
21835         var _t = this;
21836         this.timeout = setTimeout(function () {
21837             if (_t.hoverState == 'out') {
21838                 _t.hide();
21839             }
21840         }, this.delay.hide)
21841     },
21842     
21843     /**
21844      * update the position of the dialog
21845      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21846      * 
21847      *
21848      */
21849     
21850     doAlign : function()
21851     {
21852         
21853         if (this.alignEl) {
21854             this.updatePosition(this.placement, true);
21855              
21856         } else {
21857             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21858             var es = this.el.getSize();
21859             var x = Roo.lib.Dom.getViewWidth()/2;
21860             var y = Roo.lib.Dom.getViewHeight()/2;
21861             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21862             
21863         }
21864
21865          
21866          
21867         
21868         
21869     },
21870     
21871     /**
21872      * Show the popover
21873      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21874      * @param {string} (left|right|top|bottom) position
21875      */
21876     show : function (on_el, placement)
21877     {
21878         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21879         on_el = on_el || false; // default to false
21880          
21881         if (!on_el) {
21882             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21883                 on_el = this.parent().el;
21884             } else if (this.over) {
21885                 on_el = Roo.get(this.over);
21886             }
21887             
21888         }
21889         
21890         this.alignEl = Roo.get( on_el );
21891
21892         if (!this.el) {
21893             this.render(document.body);
21894         }
21895         
21896         
21897          
21898         
21899         if (this.title === false) {
21900             this.headerEl.hide();
21901         }
21902         
21903        
21904         this.el.show();
21905         this.el.dom.style.display = 'block';
21906          
21907         this.doAlign();
21908         
21909         //var arrow = this.el.select('.arrow',true).first();
21910         //arrow.set(align[2], 
21911         
21912         this.el.addClass('in');
21913         
21914          
21915         
21916         this.hoverState = 'in';
21917         
21918         if (this.modal) {
21919             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21920             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21921             this.maskEl.dom.style.display = 'block';
21922             this.maskEl.addClass('show');
21923         }
21924         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21925  
21926         this.fireEvent('show', this);
21927         
21928     },
21929     /**
21930      * fire this manually after loading a grid in the table for example
21931      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21932      * @param {Boolean} try and move it if we cant get right position.
21933      */
21934     updatePosition : function(placement, try_move)
21935     {
21936         // allow for calling with no parameters
21937         placement = placement   ? placement :  this.placement;
21938         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21939         
21940         this.el.removeClass([
21941             'fade','top','bottom', 'left', 'right','in',
21942             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21943         ]);
21944         this.el.addClass(placement + ' bs-popover-' + placement);
21945         
21946         if (!this.alignEl ) {
21947             return false;
21948         }
21949         
21950         switch (placement) {
21951             case 'right':
21952                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21953                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21954                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21955                     //normal display... or moved up/down.
21956                     this.el.setXY(offset);
21957                     var xy = this.alignEl.getAnchorXY('tr', false);
21958                     xy[0]+=2;xy[1]+=5;
21959                     this.arrowEl.setXY(xy);
21960                     return true;
21961                 }
21962                 // continue through...
21963                 return this.updatePosition('left', false);
21964                 
21965             
21966             case 'left':
21967                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21968                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21969                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21970                     //normal display... or moved up/down.
21971                     this.el.setXY(offset);
21972                     var xy = this.alignEl.getAnchorXY('tl', false);
21973                     xy[0]-=10;xy[1]+=5; // << fix me
21974                     this.arrowEl.setXY(xy);
21975                     return true;
21976                 }
21977                 // call self...
21978                 return this.updatePosition('right', false);
21979             
21980             case 'top':
21981                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21982                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21983                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21984                     //normal display... or moved up/down.
21985                     this.el.setXY(offset);
21986                     var xy = this.alignEl.getAnchorXY('t', false);
21987                     xy[1]-=10; // << fix me
21988                     this.arrowEl.setXY(xy);
21989                     return true;
21990                 }
21991                 // fall through
21992                return this.updatePosition('bottom', false);
21993             
21994             case 'bottom':
21995                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21996                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21997                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21998                     //normal display... or moved up/down.
21999                     this.el.setXY(offset);
22000                     var xy = this.alignEl.getAnchorXY('b', false);
22001                      xy[1]+=2; // << fix me
22002                     this.arrowEl.setXY(xy);
22003                     return true;
22004                 }
22005                 // fall through
22006                 return this.updatePosition('top', false);
22007                 
22008             
22009         }
22010         
22011         
22012         return false;
22013     },
22014     
22015     hide : function()
22016     {
22017         this.el.setXY([0,0]);
22018         this.el.removeClass('in');
22019         this.el.hide();
22020         this.hoverState = null;
22021         this.maskEl.hide(); // always..
22022         this.fireEvent('hide', this);
22023     }
22024     
22025 });
22026
22027
22028 Roo.apply(Roo.bootstrap.Popover, {
22029
22030     alignment : {
22031         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22032         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22033         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22034         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22035     },
22036     
22037     zIndex : 20001,
22038
22039     clickHander : false,
22040     
22041     
22042
22043     onMouseDown : function(e)
22044     {
22045         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22046             /// what is nothing is showing..
22047             this.hideAll();
22048         }
22049          
22050     },
22051     
22052     
22053     popups : [],
22054     
22055     register : function(popup)
22056     {
22057         if (!Roo.bootstrap.Popover.clickHandler) {
22058             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22059         }
22060         // hide other popups.
22061         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22062         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22063         this.hideAll(); //<< why?
22064         //this.popups.push(popup);
22065     },
22066     hideAll : function()
22067     {
22068         this.popups.forEach(function(p) {
22069             p.hide();
22070         });
22071     },
22072     onShow : function() {
22073         Roo.bootstrap.Popover.popups.push(this);
22074     },
22075     onHide : function() {
22076         Roo.bootstrap.Popover.popups.remove(this);
22077     } 
22078
22079 });
22080 /**
22081  * @class Roo.bootstrap.PopoverNav
22082  * @extends Roo.bootstrap.nav.Simplebar
22083  * @parent Roo.bootstrap.Popover
22084  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22085  * @licence LGPL
22086  * Bootstrap Popover header navigation class
22087  * FIXME? should this go under nav?
22088  *
22089  * 
22090  * @constructor
22091  * Create a new Popover Header Navigation 
22092  * @param {Object} config The config object
22093  */
22094
22095 Roo.bootstrap.PopoverNav = function(config){
22096     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22097 };
22098
22099 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22100     
22101     
22102     container_method : 'getPopoverHeader' 
22103     
22104      
22105     
22106     
22107    
22108 });
22109
22110  
22111
22112  /*
22113  * - LGPL
22114  *
22115  * Progress
22116  * 
22117  */
22118
22119 /**
22120  * @class Roo.bootstrap.Progress
22121  * @extends Roo.bootstrap.Component
22122  * @children Roo.bootstrap.ProgressBar
22123  * Bootstrap Progress class
22124  * @cfg {Boolean} striped striped of the progress bar
22125  * @cfg {Boolean} active animated of the progress bar
22126  * 
22127  * 
22128  * @constructor
22129  * Create a new Progress
22130  * @param {Object} config The config object
22131  */
22132
22133 Roo.bootstrap.Progress = function(config){
22134     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22135 };
22136
22137 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22138     
22139     striped : false,
22140     active: false,
22141     
22142     getAutoCreate : function(){
22143         var cfg = {
22144             tag: 'div',
22145             cls: 'progress'
22146         };
22147         
22148         
22149         if(this.striped){
22150             cfg.cls += ' progress-striped';
22151         }
22152       
22153         if(this.active){
22154             cfg.cls += ' active';
22155         }
22156         
22157         
22158         return cfg;
22159     }
22160    
22161 });
22162
22163  
22164
22165  /*
22166  * - LGPL
22167  *
22168  * ProgressBar
22169  * 
22170  */
22171
22172 /**
22173  * @class Roo.bootstrap.ProgressBar
22174  * @extends Roo.bootstrap.Component
22175  * Bootstrap ProgressBar class
22176  * @cfg {Number} aria_valuenow aria-value now
22177  * @cfg {Number} aria_valuemin aria-value min
22178  * @cfg {Number} aria_valuemax aria-value max
22179  * @cfg {String} label label for the progress bar
22180  * @cfg {String} panel (success | info | warning | danger )
22181  * @cfg {String} role role of the progress bar
22182  * @cfg {String} sr_only text
22183  * 
22184  * 
22185  * @constructor
22186  * Create a new ProgressBar
22187  * @param {Object} config The config object
22188  */
22189
22190 Roo.bootstrap.ProgressBar = function(config){
22191     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22192 };
22193
22194 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22195     
22196     aria_valuenow : 0,
22197     aria_valuemin : 0,
22198     aria_valuemax : 100,
22199     label : false,
22200     panel : false,
22201     role : false,
22202     sr_only: false,
22203     
22204     getAutoCreate : function()
22205     {
22206         
22207         var cfg = {
22208             tag: 'div',
22209             cls: 'progress-bar',
22210             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22211         };
22212         
22213         if(this.sr_only){
22214             cfg.cn = {
22215                 tag: 'span',
22216                 cls: 'sr-only',
22217                 html: this.sr_only
22218             }
22219         }
22220         
22221         if(this.role){
22222             cfg.role = this.role;
22223         }
22224         
22225         if(this.aria_valuenow){
22226             cfg['aria-valuenow'] = this.aria_valuenow;
22227         }
22228         
22229         if(this.aria_valuemin){
22230             cfg['aria-valuemin'] = this.aria_valuemin;
22231         }
22232         
22233         if(this.aria_valuemax){
22234             cfg['aria-valuemax'] = this.aria_valuemax;
22235         }
22236         
22237         if(this.label && !this.sr_only){
22238             cfg.html = this.label;
22239         }
22240         
22241         if(this.panel){
22242             cfg.cls += ' progress-bar-' + this.panel;
22243         }
22244         
22245         return cfg;
22246     },
22247     
22248     update : function(aria_valuenow)
22249     {
22250         this.aria_valuenow = aria_valuenow;
22251         
22252         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22253     }
22254    
22255 });
22256
22257  
22258
22259  /**
22260  * @class Roo.bootstrap.TabGroup
22261  * @extends Roo.bootstrap.Column
22262  * @children Roo.bootstrap.TabPanel
22263  * Bootstrap Column class
22264  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22265  * @cfg {Boolean} carousel true to make the group behave like a carousel
22266  * @cfg {Boolean} bullets show bullets for the panels
22267  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22268  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22269  * @cfg {Boolean} showarrow (true|false) show arrow default true
22270  * 
22271  * @constructor
22272  * Create a new TabGroup
22273  * @param {Object} config The config object
22274  */
22275
22276 Roo.bootstrap.TabGroup = function(config){
22277     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22278     if (!this.navId) {
22279         this.navId = Roo.id();
22280     }
22281     this.tabs = [];
22282     Roo.bootstrap.TabGroup.register(this);
22283     
22284 };
22285
22286 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22287     
22288     carousel : false,
22289     transition : false,
22290     bullets : 0,
22291     timer : 0,
22292     autoslide : false,
22293     slideFn : false,
22294     slideOnTouch : false,
22295     showarrow : true,
22296     
22297     getAutoCreate : function()
22298     {
22299         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22300         
22301         cfg.cls += ' tab-content';
22302         
22303         if (this.carousel) {
22304             cfg.cls += ' carousel slide';
22305             
22306             cfg.cn = [{
22307                cls : 'carousel-inner',
22308                cn : []
22309             }];
22310         
22311             if(this.bullets  && !Roo.isTouch){
22312                 
22313                 var bullets = {
22314                     cls : 'carousel-bullets',
22315                     cn : []
22316                 };
22317                
22318                 if(this.bullets_cls){
22319                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22320                 }
22321                 
22322                 bullets.cn.push({
22323                     cls : 'clear'
22324                 });
22325                 
22326                 cfg.cn[0].cn.push(bullets);
22327             }
22328             
22329             if(this.showarrow){
22330                 cfg.cn[0].cn.push({
22331                     tag : 'div',
22332                     class : 'carousel-arrow',
22333                     cn : [
22334                         {
22335                             tag : 'div',
22336                             class : 'carousel-prev',
22337                             cn : [
22338                                 {
22339                                     tag : 'i',
22340                                     class : 'fa fa-chevron-left'
22341                                 }
22342                             ]
22343                         },
22344                         {
22345                             tag : 'div',
22346                             class : 'carousel-next',
22347                             cn : [
22348                                 {
22349                                     tag : 'i',
22350                                     class : 'fa fa-chevron-right'
22351                                 }
22352                             ]
22353                         }
22354                     ]
22355                 });
22356             }
22357             
22358         }
22359         
22360         return cfg;
22361     },
22362     
22363     initEvents:  function()
22364     {
22365 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22366 //            this.el.on("touchstart", this.onTouchStart, this);
22367 //        }
22368         
22369         if(this.autoslide){
22370             var _this = this;
22371             
22372             this.slideFn = window.setInterval(function() {
22373                 _this.showPanelNext();
22374             }, this.timer);
22375         }
22376         
22377         if(this.showarrow){
22378             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22379             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22380         }
22381         
22382         
22383     },
22384     
22385 //    onTouchStart : function(e, el, o)
22386 //    {
22387 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22388 //            return;
22389 //        }
22390 //        
22391 //        this.showPanelNext();
22392 //    },
22393     
22394     
22395     getChildContainer : function()
22396     {
22397         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22398     },
22399     
22400     /**
22401     * register a Navigation item
22402     * @param {Roo.bootstrap.nav.Item} the navitem to add
22403     */
22404     register : function(item)
22405     {
22406         this.tabs.push( item);
22407         item.navId = this.navId; // not really needed..
22408         this.addBullet();
22409     
22410     },
22411     
22412     getActivePanel : function()
22413     {
22414         var r = false;
22415         Roo.each(this.tabs, function(t) {
22416             if (t.active) {
22417                 r = t;
22418                 return false;
22419             }
22420             return null;
22421         });
22422         return r;
22423         
22424     },
22425     getPanelByName : function(n)
22426     {
22427         var r = false;
22428         Roo.each(this.tabs, function(t) {
22429             if (t.tabId == n) {
22430                 r = t;
22431                 return false;
22432             }
22433             return null;
22434         });
22435         return r;
22436     },
22437     indexOfPanel : function(p)
22438     {
22439         var r = false;
22440         Roo.each(this.tabs, function(t,i) {
22441             if (t.tabId == p.tabId) {
22442                 r = i;
22443                 return false;
22444             }
22445             return null;
22446         });
22447         return r;
22448     },
22449     /**
22450      * show a specific panel
22451      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22452      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22453      */
22454     showPanel : function (pan)
22455     {
22456         if(this.transition || typeof(pan) == 'undefined'){
22457             Roo.log("waiting for the transitionend");
22458             return false;
22459         }
22460         
22461         if (typeof(pan) == 'number') {
22462             pan = this.tabs[pan];
22463         }
22464         
22465         if (typeof(pan) == 'string') {
22466             pan = this.getPanelByName(pan);
22467         }
22468         
22469         var cur = this.getActivePanel();
22470         
22471         if(!pan || !cur){
22472             Roo.log('pan or acitve pan is undefined');
22473             return false;
22474         }
22475         
22476         if (pan.tabId == this.getActivePanel().tabId) {
22477             return true;
22478         }
22479         
22480         if (false === cur.fireEvent('beforedeactivate')) {
22481             return false;
22482         }
22483         
22484         if(this.bullets > 0 && !Roo.isTouch){
22485             this.setActiveBullet(this.indexOfPanel(pan));
22486         }
22487         
22488         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22489             
22490             //class="carousel-item carousel-item-next carousel-item-left"
22491             
22492             this.transition = true;
22493             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22494             var lr = dir == 'next' ? 'left' : 'right';
22495             pan.el.addClass(dir); // or prev
22496             pan.el.addClass('carousel-item-' + dir); // or prev
22497             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22498             cur.el.addClass(lr); // or right
22499             pan.el.addClass(lr);
22500             cur.el.addClass('carousel-item-' +lr); // or right
22501             pan.el.addClass('carousel-item-' +lr);
22502             
22503             
22504             var _this = this;
22505             cur.el.on('transitionend', function() {
22506                 Roo.log("trans end?");
22507                 
22508                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22509                 pan.setActive(true);
22510                 
22511                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22512                 cur.setActive(false);
22513                 
22514                 _this.transition = false;
22515                 
22516             }, this, { single:  true } );
22517             
22518             return true;
22519         }
22520         
22521         cur.setActive(false);
22522         pan.setActive(true);
22523         
22524         return true;
22525         
22526     },
22527     showPanelNext : function()
22528     {
22529         var i = this.indexOfPanel(this.getActivePanel());
22530         
22531         if (i >= this.tabs.length - 1 && !this.autoslide) {
22532             return;
22533         }
22534         
22535         if (i >= this.tabs.length - 1 && this.autoslide) {
22536             i = -1;
22537         }
22538         
22539         this.showPanel(this.tabs[i+1]);
22540     },
22541     
22542     showPanelPrev : function()
22543     {
22544         var i = this.indexOfPanel(this.getActivePanel());
22545         
22546         if (i  < 1 && !this.autoslide) {
22547             return;
22548         }
22549         
22550         if (i < 1 && this.autoslide) {
22551             i = this.tabs.length;
22552         }
22553         
22554         this.showPanel(this.tabs[i-1]);
22555     },
22556     
22557     
22558     addBullet: function()
22559     {
22560         if(!this.bullets || Roo.isTouch){
22561             return;
22562         }
22563         var ctr = this.el.select('.carousel-bullets',true).first();
22564         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22565         var bullet = ctr.createChild({
22566             cls : 'bullet bullet-' + i
22567         },ctr.dom.lastChild);
22568         
22569         
22570         var _this = this;
22571         
22572         bullet.on('click', (function(e, el, o, ii, t){
22573
22574             e.preventDefault();
22575
22576             this.showPanel(ii);
22577
22578             if(this.autoslide && this.slideFn){
22579                 clearInterval(this.slideFn);
22580                 this.slideFn = window.setInterval(function() {
22581                     _this.showPanelNext();
22582                 }, this.timer);
22583             }
22584
22585         }).createDelegate(this, [i, bullet], true));
22586                 
22587         
22588     },
22589      
22590     setActiveBullet : function(i)
22591     {
22592         if(Roo.isTouch){
22593             return;
22594         }
22595         
22596         Roo.each(this.el.select('.bullet', true).elements, function(el){
22597             el.removeClass('selected');
22598         });
22599
22600         var bullet = this.el.select('.bullet-' + i, true).first();
22601         
22602         if(!bullet){
22603             return;
22604         }
22605         
22606         bullet.addClass('selected');
22607     }
22608     
22609     
22610   
22611 });
22612
22613  
22614
22615  
22616  
22617 Roo.apply(Roo.bootstrap.TabGroup, {
22618     
22619     groups: {},
22620      /**
22621     * register a Navigation Group
22622     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22623     */
22624     register : function(navgrp)
22625     {
22626         this.groups[navgrp.navId] = navgrp;
22627         
22628     },
22629     /**
22630     * fetch a Navigation Group based on the navigation ID
22631     * if one does not exist , it will get created.
22632     * @param {string} the navgroup to add
22633     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22634     */
22635     get: function(navId) {
22636         if (typeof(this.groups[navId]) == 'undefined') {
22637             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22638         }
22639         return this.groups[navId] ;
22640     }
22641     
22642     
22643     
22644 });
22645
22646  /*
22647  * - LGPL
22648  *
22649  * TabPanel
22650  * 
22651  */
22652
22653 /**
22654  * @class Roo.bootstrap.TabPanel
22655  * @extends Roo.bootstrap.Component
22656  * @children Roo.bootstrap.Component
22657  * Bootstrap TabPanel class
22658  * @cfg {Boolean} active panel active
22659  * @cfg {String} html panel content
22660  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22661  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22662  * @cfg {String} href click to link..
22663  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22664  * 
22665  * 
22666  * @constructor
22667  * Create a new TabPanel
22668  * @param {Object} config The config object
22669  */
22670
22671 Roo.bootstrap.TabPanel = function(config){
22672     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22673     this.addEvents({
22674         /**
22675              * @event changed
22676              * Fires when the active status changes
22677              * @param {Roo.bootstrap.TabPanel} this
22678              * @param {Boolean} state the new state
22679             
22680          */
22681         'changed': true,
22682         /**
22683              * @event beforedeactivate
22684              * Fires before a tab is de-activated - can be used to do validation on a form.
22685              * @param {Roo.bootstrap.TabPanel} this
22686              * @return {Boolean} false if there is an error
22687             
22688          */
22689         'beforedeactivate': true
22690      });
22691     
22692     this.tabId = this.tabId || Roo.id();
22693   
22694 };
22695
22696 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22697     
22698     active: false,
22699     html: false,
22700     tabId: false,
22701     navId : false,
22702     href : '',
22703     touchSlide : false,
22704     getAutoCreate : function(){
22705         
22706         
22707         var cfg = {
22708             tag: 'div',
22709             // item is needed for carousel - not sure if it has any effect otherwise
22710             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22711             html: this.html || ''
22712         };
22713         
22714         if(this.active){
22715             cfg.cls += ' active';
22716         }
22717         
22718         if(this.tabId){
22719             cfg.tabId = this.tabId;
22720         }
22721         
22722         
22723         
22724         return cfg;
22725     },
22726     
22727     initEvents:  function()
22728     {
22729         var p = this.parent();
22730         
22731         this.navId = this.navId || p.navId;
22732         
22733         if (typeof(this.navId) != 'undefined') {
22734             // not really needed.. but just in case.. parent should be a NavGroup.
22735             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22736             
22737             tg.register(this);
22738             
22739             var i = tg.tabs.length - 1;
22740             
22741             if(this.active && tg.bullets > 0 && i < tg.bullets){
22742                 tg.setActiveBullet(i);
22743             }
22744         }
22745         
22746         this.el.on('click', this.onClick, this);
22747         
22748         if(Roo.isTouch && this.touchSlide){
22749             this.el.on("touchstart", this.onTouchStart, this);
22750             this.el.on("touchmove", this.onTouchMove, this);
22751             this.el.on("touchend", this.onTouchEnd, this);
22752         }
22753         
22754     },
22755     
22756     onRender : function(ct, position)
22757     {
22758         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22759     },
22760     
22761     setActive : function(state)
22762     {
22763         Roo.log("panel - set active " + this.tabId + "=" + state);
22764         
22765         this.active = state;
22766         if (!state) {
22767             this.el.removeClass('active');
22768             
22769         } else  if (!this.el.hasClass('active')) {
22770             this.el.addClass('active');
22771         }
22772         
22773         this.fireEvent('changed', this, state);
22774     },
22775     
22776     onClick : function(e)
22777     {
22778         e.preventDefault();
22779         
22780         if(!this.href.length){
22781             return;
22782         }
22783         
22784         window.location.href = this.href;
22785     },
22786     
22787     startX : 0,
22788     startY : 0,
22789     endX : 0,
22790     endY : 0,
22791     swiping : false,
22792     
22793     onTouchStart : function(e)
22794     {
22795         this.swiping = false;
22796         
22797         this.startX = e.browserEvent.touches[0].clientX;
22798         this.startY = e.browserEvent.touches[0].clientY;
22799     },
22800     
22801     onTouchMove : function(e)
22802     {
22803         this.swiping = true;
22804         
22805         this.endX = e.browserEvent.touches[0].clientX;
22806         this.endY = e.browserEvent.touches[0].clientY;
22807     },
22808     
22809     onTouchEnd : function(e)
22810     {
22811         if(!this.swiping){
22812             this.onClick(e);
22813             return;
22814         }
22815         
22816         var tabGroup = this.parent();
22817         
22818         if(this.endX > this.startX){ // swiping right
22819             tabGroup.showPanelPrev();
22820             return;
22821         }
22822         
22823         if(this.startX > this.endX){ // swiping left
22824             tabGroup.showPanelNext();
22825             return;
22826         }
22827     }
22828     
22829     
22830 });
22831  
22832
22833  
22834
22835  /*
22836  * - LGPL
22837  *
22838  * DateField
22839  * 
22840  */
22841
22842 /**
22843  * @class Roo.bootstrap.form.DateField
22844  * @extends Roo.bootstrap.form.Input
22845  * Bootstrap DateField class
22846  * @cfg {Number} weekStart default 0
22847  * @cfg {String} viewMode default empty, (months|years)
22848  * @cfg {String} minViewMode default empty, (months|years)
22849  * @cfg {Number} startDate default -Infinity
22850  * @cfg {Number} endDate default Infinity
22851  * @cfg {Boolean} todayHighlight default false
22852  * @cfg {Boolean} todayBtn default false
22853  * @cfg {Boolean} calendarWeeks default false
22854  * @cfg {Object} daysOfWeekDisabled default empty
22855  * @cfg {Boolean} singleMode default false (true | false)
22856  * 
22857  * @cfg {Boolean} keyboardNavigation default true
22858  * @cfg {String} language default en
22859  * 
22860  * @constructor
22861  * Create a new DateField
22862  * @param {Object} config The config object
22863  */
22864
22865 Roo.bootstrap.form.DateField = function(config){
22866     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22867      this.addEvents({
22868             /**
22869              * @event show
22870              * Fires when this field show.
22871              * @param {Roo.bootstrap.form.DateField} this
22872              * @param {Mixed} date The date value
22873              */
22874             show : true,
22875             /**
22876              * @event show
22877              * Fires when this field hide.
22878              * @param {Roo.bootstrap.form.DateField} this
22879              * @param {Mixed} date The date value
22880              */
22881             hide : true,
22882             /**
22883              * @event select
22884              * Fires when select a date.
22885              * @param {Roo.bootstrap.form.DateField} this
22886              * @param {Mixed} date The date value
22887              */
22888             select : true,
22889             /**
22890              * @event beforeselect
22891              * Fires when before select a date.
22892              * @param {Roo.bootstrap.form.DateField} this
22893              * @param {Mixed} date The date value
22894              */
22895             beforeselect : true
22896         });
22897 };
22898
22899 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22900     
22901     /**
22902      * @cfg {String} format
22903      * The default date format string which can be overriden for localization support.  The format must be
22904      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22905      */
22906     format : "m/d/y",
22907     /**
22908      * @cfg {String} altFormats
22909      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22910      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22911      */
22912     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22913     
22914     weekStart : 0,
22915     
22916     viewMode : '',
22917     
22918     minViewMode : '',
22919     
22920     todayHighlight : false,
22921     
22922     todayBtn: false,
22923     
22924     language: 'en',
22925     
22926     keyboardNavigation: true,
22927     
22928     calendarWeeks: false,
22929     
22930     startDate: -Infinity,
22931     
22932     endDate: Infinity,
22933     
22934     daysOfWeekDisabled: [],
22935     
22936     _events: [],
22937     
22938     singleMode : false,
22939     
22940     UTCDate: function()
22941     {
22942         return new Date(Date.UTC.apply(Date, arguments));
22943     },
22944     
22945     UTCToday: function()
22946     {
22947         var today = new Date();
22948         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22949     },
22950     
22951     getDate: function() {
22952             var d = this.getUTCDate();
22953             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22954     },
22955     
22956     getUTCDate: function() {
22957             return this.date;
22958     },
22959     
22960     setDate: function(d) {
22961             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22962     },
22963     
22964     setUTCDate: function(d) {
22965             this.date = d;
22966             this.setValue(this.formatDate(this.date));
22967     },
22968         
22969     onRender: function(ct, position)
22970     {
22971         
22972         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22973         
22974         this.language = this.language || 'en';
22975         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22976         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22977         
22978         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22979         this.format = this.format || 'm/d/y';
22980         this.isInline = false;
22981         this.isInput = true;
22982         this.component = this.el.select('.add-on', true).first() || false;
22983         this.component = (this.component && this.component.length === 0) ? false : this.component;
22984         this.hasInput = this.component && this.inputEl().length;
22985         
22986         if (typeof(this.minViewMode === 'string')) {
22987             switch (this.minViewMode) {
22988                 case 'months':
22989                     this.minViewMode = 1;
22990                     break;
22991                 case 'years':
22992                     this.minViewMode = 2;
22993                     break;
22994                 default:
22995                     this.minViewMode = 0;
22996                     break;
22997             }
22998         }
22999         
23000         if (typeof(this.viewMode === 'string')) {
23001             switch (this.viewMode) {
23002                 case 'months':
23003                     this.viewMode = 1;
23004                     break;
23005                 case 'years':
23006                     this.viewMode = 2;
23007                     break;
23008                 default:
23009                     this.viewMode = 0;
23010                     break;
23011             }
23012         }
23013                 
23014         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23015         
23016 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23017         
23018         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23019         
23020         this.picker().on('mousedown', this.onMousedown, this);
23021         this.picker().on('click', this.onClick, this);
23022         
23023         this.picker().addClass('datepicker-dropdown');
23024         
23025         this.startViewMode = this.viewMode;
23026         
23027         if(this.singleMode){
23028             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23029                 v.setVisibilityMode(Roo.Element.DISPLAY);
23030                 v.hide();
23031             });
23032             
23033             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23034                 v.setStyle('width', '189px');
23035             });
23036         }
23037         
23038         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23039             if(!this.calendarWeeks){
23040                 v.remove();
23041                 return;
23042             }
23043             
23044             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23045             v.attr('colspan', function(i, val){
23046                 return parseInt(val) + 1;
23047             });
23048         });
23049                         
23050         
23051         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23052         
23053         this.setStartDate(this.startDate);
23054         this.setEndDate(this.endDate);
23055         
23056         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23057         
23058         this.fillDow();
23059         this.fillMonths();
23060         this.update();
23061         this.showMode();
23062         
23063         if(this.isInline) {
23064             this.showPopup();
23065         }
23066     },
23067     
23068     picker : function()
23069     {
23070         return this.pickerEl;
23071 //        return this.el.select('.datepicker', true).first();
23072     },
23073     
23074     fillDow: function()
23075     {
23076         var dowCnt = this.weekStart;
23077         
23078         var dow = {
23079             tag: 'tr',
23080             cn: [
23081                 
23082             ]
23083         };
23084         
23085         if(this.calendarWeeks){
23086             dow.cn.push({
23087                 tag: 'th',
23088                 cls: 'cw',
23089                 html: '&nbsp;'
23090             })
23091         }
23092         
23093         while (dowCnt < this.weekStart + 7) {
23094             dow.cn.push({
23095                 tag: 'th',
23096                 cls: 'dow',
23097                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23098             });
23099         }
23100         
23101         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23102     },
23103     
23104     fillMonths: function()
23105     {    
23106         var i = 0;
23107         var months = this.picker().select('>.datepicker-months td', true).first();
23108         
23109         months.dom.innerHTML = '';
23110         
23111         while (i < 12) {
23112             var month = {
23113                 tag: 'span',
23114                 cls: 'month',
23115                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23116             };
23117             
23118             months.createChild(month);
23119         }
23120         
23121     },
23122     
23123     update: function()
23124     {
23125         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;
23126         
23127         if (this.date < this.startDate) {
23128             this.viewDate = new Date(this.startDate);
23129         } else if (this.date > this.endDate) {
23130             this.viewDate = new Date(this.endDate);
23131         } else {
23132             this.viewDate = new Date(this.date);
23133         }
23134         
23135         this.fill();
23136     },
23137     
23138     fill: function() 
23139     {
23140         var d = new Date(this.viewDate),
23141                 year = d.getUTCFullYear(),
23142                 month = d.getUTCMonth(),
23143                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23144                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23145                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23146                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23147                 currentDate = this.date && this.date.valueOf(),
23148                 today = this.UTCToday();
23149         
23150         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23151         
23152 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23153         
23154 //        this.picker.select('>tfoot th.today').
23155 //                                              .text(dates[this.language].today)
23156 //                                              .toggle(this.todayBtn !== false);
23157     
23158         this.updateNavArrows();
23159         this.fillMonths();
23160                                                 
23161         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23162         
23163         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23164          
23165         prevMonth.setUTCDate(day);
23166         
23167         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23168         
23169         var nextMonth = new Date(prevMonth);
23170         
23171         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23172         
23173         nextMonth = nextMonth.valueOf();
23174         
23175         var fillMonths = false;
23176         
23177         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23178         
23179         while(prevMonth.valueOf() <= nextMonth) {
23180             var clsName = '';
23181             
23182             if (prevMonth.getUTCDay() === this.weekStart) {
23183                 if(fillMonths){
23184                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23185                 }
23186                     
23187                 fillMonths = {
23188                     tag: 'tr',
23189                     cn: []
23190                 };
23191                 
23192                 if(this.calendarWeeks){
23193                     // ISO 8601: First week contains first thursday.
23194                     // ISO also states week starts on Monday, but we can be more abstract here.
23195                     var
23196                     // Start of current week: based on weekstart/current date
23197                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23198                     // Thursday of this week
23199                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23200                     // First Thursday of year, year from thursday
23201                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23202                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23203                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23204                     
23205                     fillMonths.cn.push({
23206                         tag: 'td',
23207                         cls: 'cw',
23208                         html: calWeek
23209                     });
23210                 }
23211             }
23212             
23213             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23214                 clsName += ' old';
23215             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23216                 clsName += ' new';
23217             }
23218             if (this.todayHighlight &&
23219                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23220                 prevMonth.getUTCMonth() == today.getMonth() &&
23221                 prevMonth.getUTCDate() == today.getDate()) {
23222                 clsName += ' today';
23223             }
23224             
23225             if (currentDate && prevMonth.valueOf() === currentDate) {
23226                 clsName += ' active';
23227             }
23228             
23229             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23230                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23231                     clsName += ' disabled';
23232             }
23233             
23234             fillMonths.cn.push({
23235                 tag: 'td',
23236                 cls: 'day ' + clsName,
23237                 html: prevMonth.getDate()
23238             });
23239             
23240             prevMonth.setDate(prevMonth.getDate()+1);
23241         }
23242           
23243         var currentYear = this.date && this.date.getUTCFullYear();
23244         var currentMonth = this.date && this.date.getUTCMonth();
23245         
23246         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23247         
23248         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23249             v.removeClass('active');
23250             
23251             if(currentYear === year && k === currentMonth){
23252                 v.addClass('active');
23253             }
23254             
23255             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23256                 v.addClass('disabled');
23257             }
23258             
23259         });
23260         
23261         
23262         year = parseInt(year/10, 10) * 10;
23263         
23264         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23265         
23266         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23267         
23268         year -= 1;
23269         for (var i = -1; i < 11; i++) {
23270             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23271                 tag: 'span',
23272                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23273                 html: year
23274             });
23275             
23276             year += 1;
23277         }
23278     },
23279     
23280     showMode: function(dir) 
23281     {
23282         if (dir) {
23283             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23284         }
23285         
23286         Roo.each(this.picker().select('>div',true).elements, function(v){
23287             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23288             v.hide();
23289         });
23290         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23291     },
23292     
23293     place: function()
23294     {
23295         if(this.isInline) {
23296             return;
23297         }
23298         
23299         this.picker().removeClass(['bottom', 'top']);
23300         
23301         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23302             /*
23303              * place to the top of element!
23304              *
23305              */
23306             
23307             this.picker().addClass('top');
23308             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23309             
23310             return;
23311         }
23312         
23313         this.picker().addClass('bottom');
23314         
23315         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23316     },
23317     
23318     parseDate : function(value)
23319     {
23320         if(!value || value instanceof Date){
23321             return value;
23322         }
23323         var v = Date.parseDate(value, this.format);
23324         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23325             v = Date.parseDate(value, 'Y-m-d');
23326         }
23327         if(!v && this.altFormats){
23328             if(!this.altFormatsArray){
23329                 this.altFormatsArray = this.altFormats.split("|");
23330             }
23331             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23332                 v = Date.parseDate(value, this.altFormatsArray[i]);
23333             }
23334         }
23335         return v;
23336     },
23337     
23338     formatDate : function(date, fmt)
23339     {   
23340         return (!date || !(date instanceof Date)) ?
23341         date : date.dateFormat(fmt || this.format);
23342     },
23343     
23344     onFocus : function()
23345     {
23346         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23347         this.showPopup();
23348     },
23349     
23350     onBlur : function()
23351     {
23352         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23353         
23354         var d = this.inputEl().getValue();
23355         
23356         this.setValue(d);
23357                 
23358         this.hidePopup();
23359     },
23360     
23361     showPopup : function()
23362     {
23363         this.picker().show();
23364         this.update();
23365         this.place();
23366         
23367         this.fireEvent('showpopup', this, this.date);
23368     },
23369     
23370     hidePopup : function()
23371     {
23372         if(this.isInline) {
23373             return;
23374         }
23375         this.picker().hide();
23376         this.viewMode = this.startViewMode;
23377         this.showMode();
23378         
23379         this.fireEvent('hidepopup', this, this.date);
23380         
23381     },
23382     
23383     onMousedown: function(e)
23384     {
23385         e.stopPropagation();
23386         e.preventDefault();
23387     },
23388     
23389     keyup: function(e)
23390     {
23391         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23392         this.update();
23393     },
23394
23395     setValue: function(v)
23396     {
23397         if(this.fireEvent('beforeselect', this, v) !== false){
23398             var d = new Date(this.parseDate(v) ).clearTime();
23399         
23400             if(isNaN(d.getTime())){
23401                 this.date = this.viewDate = '';
23402                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23403                 return;
23404             }
23405
23406             v = this.formatDate(d);
23407
23408             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23409
23410             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23411
23412             this.update();
23413
23414             this.fireEvent('select', this, this.date);
23415         }
23416     },
23417     
23418     getValue: function()
23419     {
23420         return this.formatDate(this.date);
23421     },
23422     
23423     fireKey: function(e)
23424     {
23425         if (!this.picker().isVisible()){
23426             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23427                 this.showPopup();
23428             }
23429             return;
23430         }
23431         
23432         var dateChanged = false,
23433         dir, day, month,
23434         newDate, newViewDate;
23435         
23436         switch(e.keyCode){
23437             case 27: // escape
23438                 this.hidePopup();
23439                 e.preventDefault();
23440                 break;
23441             case 37: // left
23442             case 39: // right
23443                 if (!this.keyboardNavigation) {
23444                     break;
23445                 }
23446                 dir = e.keyCode == 37 ? -1 : 1;
23447                 
23448                 if (e.ctrlKey){
23449                     newDate = this.moveYear(this.date, dir);
23450                     newViewDate = this.moveYear(this.viewDate, dir);
23451                 } else if (e.shiftKey){
23452                     newDate = this.moveMonth(this.date, dir);
23453                     newViewDate = this.moveMonth(this.viewDate, dir);
23454                 } else {
23455                     newDate = new Date(this.date);
23456                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23457                     newViewDate = new Date(this.viewDate);
23458                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23459                 }
23460                 if (this.dateWithinRange(newDate)){
23461                     this.date = newDate;
23462                     this.viewDate = newViewDate;
23463                     this.setValue(this.formatDate(this.date));
23464 //                    this.update();
23465                     e.preventDefault();
23466                     dateChanged = true;
23467                 }
23468                 break;
23469             case 38: // up
23470             case 40: // down
23471                 if (!this.keyboardNavigation) {
23472                     break;
23473                 }
23474                 dir = e.keyCode == 38 ? -1 : 1;
23475                 if (e.ctrlKey){
23476                     newDate = this.moveYear(this.date, dir);
23477                     newViewDate = this.moveYear(this.viewDate, dir);
23478                 } else if (e.shiftKey){
23479                     newDate = this.moveMonth(this.date, dir);
23480                     newViewDate = this.moveMonth(this.viewDate, dir);
23481                 } else {
23482                     newDate = new Date(this.date);
23483                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23484                     newViewDate = new Date(this.viewDate);
23485                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23486                 }
23487                 if (this.dateWithinRange(newDate)){
23488                     this.date = newDate;
23489                     this.viewDate = newViewDate;
23490                     this.setValue(this.formatDate(this.date));
23491 //                    this.update();
23492                     e.preventDefault();
23493                     dateChanged = true;
23494                 }
23495                 break;
23496             case 13: // enter
23497                 this.setValue(this.formatDate(this.date));
23498                 this.hidePopup();
23499                 e.preventDefault();
23500                 break;
23501             case 9: // tab
23502                 this.setValue(this.formatDate(this.date));
23503                 this.hidePopup();
23504                 break;
23505             case 16: // shift
23506             case 17: // ctrl
23507             case 18: // alt
23508                 break;
23509             default :
23510                 this.hidePopup();
23511                 
23512         }
23513     },
23514     
23515     
23516     onClick: function(e) 
23517     {
23518         e.stopPropagation();
23519         e.preventDefault();
23520         
23521         var target = e.getTarget();
23522         
23523         if(target.nodeName.toLowerCase() === 'i'){
23524             target = Roo.get(target).dom.parentNode;
23525         }
23526         
23527         var nodeName = target.nodeName;
23528         var className = target.className;
23529         var html = target.innerHTML;
23530         //Roo.log(nodeName);
23531         
23532         switch(nodeName.toLowerCase()) {
23533             case 'th':
23534                 switch(className) {
23535                     case 'switch':
23536                         this.showMode(1);
23537                         break;
23538                     case 'prev':
23539                     case 'next':
23540                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23541                         switch(this.viewMode){
23542                                 case 0:
23543                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23544                                         break;
23545                                 case 1:
23546                                 case 2:
23547                                         this.viewDate = this.moveYear(this.viewDate, dir);
23548                                         break;
23549                         }
23550                         this.fill();
23551                         break;
23552                     case 'today':
23553                         var date = new Date();
23554                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23555 //                        this.fill()
23556                         this.setValue(this.formatDate(this.date));
23557                         
23558                         this.hidePopup();
23559                         break;
23560                 }
23561                 break;
23562             case 'span':
23563                 if (className.indexOf('disabled') < 0) {
23564                 if (!this.viewDate) {
23565                     this.viewDate = new Date();
23566                 }
23567                 this.viewDate.setUTCDate(1);
23568                     if (className.indexOf('month') > -1) {
23569                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23570                     } else {
23571                         var year = parseInt(html, 10) || 0;
23572                         this.viewDate.setUTCFullYear(year);
23573                         
23574                     }
23575                     
23576                     if(this.singleMode){
23577                         this.setValue(this.formatDate(this.viewDate));
23578                         this.hidePopup();
23579                         return;
23580                     }
23581                     
23582                     this.showMode(-1);
23583                     this.fill();
23584                 }
23585                 break;
23586                 
23587             case 'td':
23588                 //Roo.log(className);
23589                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23590                     var day = parseInt(html, 10) || 1;
23591                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23592                         month = (this.viewDate || new Date()).getUTCMonth();
23593
23594                     if (className.indexOf('old') > -1) {
23595                         if(month === 0 ){
23596                             month = 11;
23597                             year -= 1;
23598                         }else{
23599                             month -= 1;
23600                         }
23601                     } else if (className.indexOf('new') > -1) {
23602                         if (month == 11) {
23603                             month = 0;
23604                             year += 1;
23605                         } else {
23606                             month += 1;
23607                         }
23608                     }
23609                     //Roo.log([year,month,day]);
23610                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23611                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23612 //                    this.fill();
23613                     //Roo.log(this.formatDate(this.date));
23614                     this.setValue(this.formatDate(this.date));
23615                     this.hidePopup();
23616                 }
23617                 break;
23618         }
23619     },
23620     
23621     setStartDate: function(startDate)
23622     {
23623         this.startDate = startDate || -Infinity;
23624         if (this.startDate !== -Infinity) {
23625             this.startDate = this.parseDate(this.startDate);
23626         }
23627         this.update();
23628         this.updateNavArrows();
23629     },
23630
23631     setEndDate: function(endDate)
23632     {
23633         this.endDate = endDate || Infinity;
23634         if (this.endDate !== Infinity) {
23635             this.endDate = this.parseDate(this.endDate);
23636         }
23637         this.update();
23638         this.updateNavArrows();
23639     },
23640     
23641     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23642     {
23643         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23644         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23645             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23646         }
23647         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23648             return parseInt(d, 10);
23649         });
23650         this.update();
23651         this.updateNavArrows();
23652     },
23653     
23654     updateNavArrows: function() 
23655     {
23656         if(this.singleMode){
23657             return;
23658         }
23659         
23660         var d = new Date(this.viewDate),
23661         year = d.getUTCFullYear(),
23662         month = d.getUTCMonth();
23663         
23664         Roo.each(this.picker().select('.prev', true).elements, function(v){
23665             v.show();
23666             switch (this.viewMode) {
23667                 case 0:
23668
23669                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23670                         v.hide();
23671                     }
23672                     break;
23673                 case 1:
23674                 case 2:
23675                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23676                         v.hide();
23677                     }
23678                     break;
23679             }
23680         });
23681         
23682         Roo.each(this.picker().select('.next', true).elements, function(v){
23683             v.show();
23684             switch (this.viewMode) {
23685                 case 0:
23686
23687                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23688                         v.hide();
23689                     }
23690                     break;
23691                 case 1:
23692                 case 2:
23693                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23694                         v.hide();
23695                     }
23696                     break;
23697             }
23698         })
23699     },
23700     
23701     moveMonth: function(date, dir)
23702     {
23703         if (!dir) {
23704             return date;
23705         }
23706         var new_date = new Date(date.valueOf()),
23707         day = new_date.getUTCDate(),
23708         month = new_date.getUTCMonth(),
23709         mag = Math.abs(dir),
23710         new_month, test;
23711         dir = dir > 0 ? 1 : -1;
23712         if (mag == 1){
23713             test = dir == -1
23714             // If going back one month, make sure month is not current month
23715             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23716             ? function(){
23717                 return new_date.getUTCMonth() == month;
23718             }
23719             // If going forward one month, make sure month is as expected
23720             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23721             : function(){
23722                 return new_date.getUTCMonth() != new_month;
23723             };
23724             new_month = month + dir;
23725             new_date.setUTCMonth(new_month);
23726             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23727             if (new_month < 0 || new_month > 11) {
23728                 new_month = (new_month + 12) % 12;
23729             }
23730         } else {
23731             // For magnitudes >1, move one month at a time...
23732             for (var i=0; i<mag; i++) {
23733                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23734                 new_date = this.moveMonth(new_date, dir);
23735             }
23736             // ...then reset the day, keeping it in the new month
23737             new_month = new_date.getUTCMonth();
23738             new_date.setUTCDate(day);
23739             test = function(){
23740                 return new_month != new_date.getUTCMonth();
23741             };
23742         }
23743         // Common date-resetting loop -- if date is beyond end of month, make it
23744         // end of month
23745         while (test()){
23746             new_date.setUTCDate(--day);
23747             new_date.setUTCMonth(new_month);
23748         }
23749         return new_date;
23750     },
23751
23752     moveYear: function(date, dir)
23753     {
23754         return this.moveMonth(date, dir*12);
23755     },
23756
23757     dateWithinRange: function(date)
23758     {
23759         return date >= this.startDate && date <= this.endDate;
23760     },
23761
23762     
23763     remove: function() 
23764     {
23765         this.picker().remove();
23766     },
23767     
23768     validateValue : function(value)
23769     {
23770         if(this.getVisibilityEl().hasClass('hidden')){
23771             return true;
23772         }
23773         
23774         if(value.length < 1)  {
23775             if(this.allowBlank){
23776                 return true;
23777             }
23778             return false;
23779         }
23780         
23781         if(value.length < this.minLength){
23782             return false;
23783         }
23784         if(value.length > this.maxLength){
23785             return false;
23786         }
23787         if(this.vtype){
23788             var vt = Roo.form.VTypes;
23789             if(!vt[this.vtype](value, this)){
23790                 return false;
23791             }
23792         }
23793         if(typeof this.validator == "function"){
23794             var msg = this.validator(value);
23795             if(msg !== true){
23796                 return false;
23797             }
23798         }
23799         
23800         if(this.regex && !this.regex.test(value)){
23801             return false;
23802         }
23803         
23804         if(typeof(this.parseDate(value)) == 'undefined'){
23805             return false;
23806         }
23807         
23808         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23809             return false;
23810         }      
23811         
23812         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23813             return false;
23814         } 
23815         
23816         
23817         return true;
23818     },
23819     
23820     reset : function()
23821     {
23822         this.date = this.viewDate = '';
23823         
23824         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23825     }
23826    
23827 });
23828
23829 Roo.apply(Roo.bootstrap.form.DateField,  {
23830     
23831     head : {
23832         tag: 'thead',
23833         cn: [
23834         {
23835             tag: 'tr',
23836             cn: [
23837             {
23838                 tag: 'th',
23839                 cls: 'prev',
23840                 html: '<i class="fa fa-arrow-left"/>'
23841             },
23842             {
23843                 tag: 'th',
23844                 cls: 'switch',
23845                 colspan: '5'
23846             },
23847             {
23848                 tag: 'th',
23849                 cls: 'next',
23850                 html: '<i class="fa fa-arrow-right"/>'
23851             }
23852
23853             ]
23854         }
23855         ]
23856     },
23857     
23858     content : {
23859         tag: 'tbody',
23860         cn: [
23861         {
23862             tag: 'tr',
23863             cn: [
23864             {
23865                 tag: 'td',
23866                 colspan: '7'
23867             }
23868             ]
23869         }
23870         ]
23871     },
23872     
23873     footer : {
23874         tag: 'tfoot',
23875         cn: [
23876         {
23877             tag: 'tr',
23878             cn: [
23879             {
23880                 tag: 'th',
23881                 colspan: '7',
23882                 cls: 'today'
23883             }
23884                     
23885             ]
23886         }
23887         ]
23888     },
23889     
23890     dates:{
23891         en: {
23892             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23893             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23894             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23895             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23896             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23897             today: "Today"
23898         }
23899     },
23900     
23901     modes: [
23902     {
23903         clsName: 'days',
23904         navFnc: 'Month',
23905         navStep: 1
23906     },
23907     {
23908         clsName: 'months',
23909         navFnc: 'FullYear',
23910         navStep: 1
23911     },
23912     {
23913         clsName: 'years',
23914         navFnc: 'FullYear',
23915         navStep: 10
23916     }]
23917 });
23918
23919 Roo.apply(Roo.bootstrap.form.DateField,  {
23920   
23921     template : {
23922         tag: 'div',
23923         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23924         cn: [
23925         {
23926             tag: 'div',
23927             cls: 'datepicker-days',
23928             cn: [
23929             {
23930                 tag: 'table',
23931                 cls: 'table-condensed',
23932                 cn:[
23933                 Roo.bootstrap.form.DateField.head,
23934                 {
23935                     tag: 'tbody'
23936                 },
23937                 Roo.bootstrap.form.DateField.footer
23938                 ]
23939             }
23940             ]
23941         },
23942         {
23943             tag: 'div',
23944             cls: 'datepicker-months',
23945             cn: [
23946             {
23947                 tag: 'table',
23948                 cls: 'table-condensed',
23949                 cn:[
23950                 Roo.bootstrap.form.DateField.head,
23951                 Roo.bootstrap.form.DateField.content,
23952                 Roo.bootstrap.form.DateField.footer
23953                 ]
23954             }
23955             ]
23956         },
23957         {
23958             tag: 'div',
23959             cls: 'datepicker-years',
23960             cn: [
23961             {
23962                 tag: 'table',
23963                 cls: 'table-condensed',
23964                 cn:[
23965                 Roo.bootstrap.form.DateField.head,
23966                 Roo.bootstrap.form.DateField.content,
23967                 Roo.bootstrap.form.DateField.footer
23968                 ]
23969             }
23970             ]
23971         }
23972         ]
23973     }
23974 });
23975
23976  
23977
23978  /*
23979  * - LGPL
23980  *
23981  * TimeField
23982  * 
23983  */
23984
23985 /**
23986  * @class Roo.bootstrap.form.TimeField
23987  * @extends Roo.bootstrap.form.Input
23988  * Bootstrap DateField class
23989  * 
23990  * 
23991  * @constructor
23992  * Create a new TimeField
23993  * @param {Object} config The config object
23994  */
23995
23996 Roo.bootstrap.form.TimeField = function(config){
23997     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23998     this.addEvents({
23999             /**
24000              * @event show
24001              * Fires when this field show.
24002              * @param {Roo.bootstrap.form.DateField} thisthis
24003              * @param {Mixed} date The date value
24004              */
24005             show : true,
24006             /**
24007              * @event show
24008              * Fires when this field hide.
24009              * @param {Roo.bootstrap.form.DateField} this
24010              * @param {Mixed} date The date value
24011              */
24012             hide : true,
24013             /**
24014              * @event select
24015              * Fires when select a date.
24016              * @param {Roo.bootstrap.form.DateField} this
24017              * @param {Mixed} date The date value
24018              */
24019             select : true
24020         });
24021 };
24022
24023 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24024     
24025     /**
24026      * @cfg {String} format
24027      * The default time format string which can be overriden for localization support.  The format must be
24028      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24029      */
24030     format : "H:i",
24031
24032     getAutoCreate : function()
24033     {
24034         this.after = '<i class="fa far fa-clock"></i>';
24035         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24036         
24037          
24038     },
24039     onRender: function(ct, position)
24040     {
24041         
24042         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24043                 
24044         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24045         
24046         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24047         
24048         this.pop = this.picker().select('>.datepicker-time',true).first();
24049         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24050         
24051         this.picker().on('mousedown', this.onMousedown, this);
24052         this.picker().on('click', this.onClick, this);
24053         
24054         this.picker().addClass('datepicker-dropdown');
24055     
24056         this.fillTime();
24057         this.update();
24058             
24059         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24060         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24061         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24062         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24063         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24064         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24065
24066     },
24067     
24068     fireKey: function(e){
24069         if (!this.picker().isVisible()){
24070             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24071                 this.show();
24072             }
24073             return;
24074         }
24075
24076         e.preventDefault();
24077         
24078         switch(e.keyCode){
24079             case 27: // escape
24080                 this.hide();
24081                 break;
24082             case 37: // left
24083             case 39: // right
24084                 this.onTogglePeriod();
24085                 break;
24086             case 38: // up
24087                 this.onIncrementMinutes();
24088                 break;
24089             case 40: // down
24090                 this.onDecrementMinutes();
24091                 break;
24092             case 13: // enter
24093             case 9: // tab
24094                 this.setTime();
24095                 break;
24096         }
24097     },
24098     
24099     onClick: function(e) {
24100         e.stopPropagation();
24101         e.preventDefault();
24102     },
24103     
24104     picker : function()
24105     {
24106         return this.pickerEl;
24107     },
24108     
24109     fillTime: function()
24110     {    
24111         var time = this.pop.select('tbody', true).first();
24112         
24113         time.dom.innerHTML = '';
24114         
24115         time.createChild({
24116             tag: 'tr',
24117             cn: [
24118                 {
24119                     tag: 'td',
24120                     cn: [
24121                         {
24122                             tag: 'a',
24123                             href: '#',
24124                             cls: 'btn',
24125                             cn: [
24126                                 {
24127                                     tag: 'i',
24128                                     cls: 'hours-up fa fas fa-chevron-up'
24129                                 }
24130                             ]
24131                         } 
24132                     ]
24133                 },
24134                 {
24135                     tag: 'td',
24136                     cls: 'separator'
24137                 },
24138                 {
24139                     tag: 'td',
24140                     cn: [
24141                         {
24142                             tag: 'a',
24143                             href: '#',
24144                             cls: 'btn',
24145                             cn: [
24146                                 {
24147                                     tag: 'i',
24148                                     cls: 'minutes-up fa fas fa-chevron-up'
24149                                 }
24150                             ]
24151                         }
24152                     ]
24153                 },
24154                 {
24155                     tag: 'td',
24156                     cls: 'separator'
24157                 }
24158             ]
24159         });
24160         
24161         time.createChild({
24162             tag: 'tr',
24163             cn: [
24164                 {
24165                     tag: 'td',
24166                     cn: [
24167                         {
24168                             tag: 'span',
24169                             cls: 'timepicker-hour',
24170                             html: '00'
24171                         }  
24172                     ]
24173                 },
24174                 {
24175                     tag: 'td',
24176                     cls: 'separator',
24177                     html: ':'
24178                 },
24179                 {
24180                     tag: 'td',
24181                     cn: [
24182                         {
24183                             tag: 'span',
24184                             cls: 'timepicker-minute',
24185                             html: '00'
24186                         }  
24187                     ]
24188                 },
24189                 {
24190                     tag: 'td',
24191                     cls: 'separator'
24192                 },
24193                 {
24194                     tag: 'td',
24195                     cn: [
24196                         {
24197                             tag: 'button',
24198                             type: 'button',
24199                             cls: 'btn btn-primary period',
24200                             html: 'AM'
24201                             
24202                         }
24203                     ]
24204                 }
24205             ]
24206         });
24207         
24208         time.createChild({
24209             tag: 'tr',
24210             cn: [
24211                 {
24212                     tag: 'td',
24213                     cn: [
24214                         {
24215                             tag: 'a',
24216                             href: '#',
24217                             cls: 'btn',
24218                             cn: [
24219                                 {
24220                                     tag: 'span',
24221                                     cls: 'hours-down fa fas fa-chevron-down'
24222                                 }
24223                             ]
24224                         }
24225                     ]
24226                 },
24227                 {
24228                     tag: 'td',
24229                     cls: 'separator'
24230                 },
24231                 {
24232                     tag: 'td',
24233                     cn: [
24234                         {
24235                             tag: 'a',
24236                             href: '#',
24237                             cls: 'btn',
24238                             cn: [
24239                                 {
24240                                     tag: 'span',
24241                                     cls: 'minutes-down fa fas fa-chevron-down'
24242                                 }
24243                             ]
24244                         }
24245                     ]
24246                 },
24247                 {
24248                     tag: 'td',
24249                     cls: 'separator'
24250                 }
24251             ]
24252         });
24253         
24254     },
24255     
24256     update: function()
24257     {
24258         
24259         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24260         
24261         this.fill();
24262     },
24263     
24264     fill: function() 
24265     {
24266         var hours = this.time.getHours();
24267         var minutes = this.time.getMinutes();
24268         var period = 'AM';
24269         
24270         if(hours > 11){
24271             period = 'PM';
24272         }
24273         
24274         if(hours == 0){
24275             hours = 12;
24276         }
24277         
24278         
24279         if(hours > 12){
24280             hours = hours - 12;
24281         }
24282         
24283         if(hours < 10){
24284             hours = '0' + hours;
24285         }
24286         
24287         if(minutes < 10){
24288             minutes = '0' + minutes;
24289         }
24290         
24291         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24292         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24293         this.pop.select('button', true).first().dom.innerHTML = period;
24294         
24295     },
24296     
24297     place: function()
24298     {   
24299         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24300         
24301         var cls = ['bottom'];
24302         
24303         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24304             cls.pop();
24305             cls.push('top');
24306         }
24307         
24308         cls.push('right');
24309         
24310         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24311             cls.pop();
24312             cls.push('left');
24313         }
24314         //this.picker().setXY(20000,20000);
24315         this.picker().addClass(cls.join('-'));
24316         
24317         var _this = this;
24318         
24319         Roo.each(cls, function(c){
24320             if(c == 'bottom'){
24321                 (function() {
24322                  //  
24323                 }).defer(200);
24324                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24325                 //_this.picker().setTop(_this.inputEl().getHeight());
24326                 return;
24327             }
24328             if(c == 'top'){
24329                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24330                 
24331                 //_this.picker().setTop(0 - _this.picker().getHeight());
24332                 return;
24333             }
24334             /*
24335             if(c == 'left'){
24336                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24337                 return;
24338             }
24339             if(c == 'right'){
24340                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24341                 return;
24342             }
24343             */
24344         });
24345         
24346     },
24347   
24348     onFocus : function()
24349     {
24350         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24351         this.show();
24352     },
24353     
24354     onBlur : function()
24355     {
24356         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24357         this.hide();
24358     },
24359     
24360     show : function()
24361     {
24362         this.picker().show();
24363         this.pop.show();
24364         this.update();
24365         this.place();
24366         
24367         this.fireEvent('show', this, this.date);
24368     },
24369     
24370     hide : function()
24371     {
24372         this.picker().hide();
24373         this.pop.hide();
24374         
24375         this.fireEvent('hide', this, this.date);
24376     },
24377     
24378     setTime : function()
24379     {
24380         this.hide();
24381         this.setValue(this.time.format(this.format));
24382         
24383         this.fireEvent('select', this, this.date);
24384         
24385         
24386     },
24387     
24388     onMousedown: function(e){
24389         e.stopPropagation();
24390         e.preventDefault();
24391     },
24392     
24393     onIncrementHours: function()
24394     {
24395         Roo.log('onIncrementHours');
24396         this.time = this.time.add(Date.HOUR, 1);
24397         this.update();
24398         
24399     },
24400     
24401     onDecrementHours: function()
24402     {
24403         Roo.log('onDecrementHours');
24404         this.time = this.time.add(Date.HOUR, -1);
24405         this.update();
24406     },
24407     
24408     onIncrementMinutes: function()
24409     {
24410         Roo.log('onIncrementMinutes');
24411         this.time = this.time.add(Date.MINUTE, 1);
24412         this.update();
24413     },
24414     
24415     onDecrementMinutes: function()
24416     {
24417         Roo.log('onDecrementMinutes');
24418         this.time = this.time.add(Date.MINUTE, -1);
24419         this.update();
24420     },
24421     
24422     onTogglePeriod: function()
24423     {
24424         Roo.log('onTogglePeriod');
24425         this.time = this.time.add(Date.HOUR, 12);
24426         this.update();
24427     }
24428     
24429    
24430 });
24431  
24432
24433 Roo.apply(Roo.bootstrap.form.TimeField,  {
24434   
24435     template : {
24436         tag: 'div',
24437         cls: 'datepicker dropdown-menu',
24438         cn: [
24439             {
24440                 tag: 'div',
24441                 cls: 'datepicker-time',
24442                 cn: [
24443                 {
24444                     tag: 'table',
24445                     cls: 'table-condensed',
24446                     cn:[
24447                         {
24448                             tag: 'tbody',
24449                             cn: [
24450                                 {
24451                                     tag: 'tr',
24452                                     cn: [
24453                                     {
24454                                         tag: 'td',
24455                                         colspan: '7'
24456                                     }
24457                                     ]
24458                                 }
24459                             ]
24460                         },
24461                         {
24462                             tag: 'tfoot',
24463                             cn: [
24464                                 {
24465                                     tag: 'tr',
24466                                     cn: [
24467                                     {
24468                                         tag: 'th',
24469                                         colspan: '7',
24470                                         cls: '',
24471                                         cn: [
24472                                             {
24473                                                 tag: 'button',
24474                                                 cls: 'btn btn-info ok',
24475                                                 html: 'OK'
24476                                             }
24477                                         ]
24478                                     }
24479                     
24480                                     ]
24481                                 }
24482                             ]
24483                         }
24484                     ]
24485                 }
24486                 ]
24487             }
24488         ]
24489     }
24490 });
24491
24492  
24493
24494  /*
24495  * - LGPL
24496  *
24497  * MonthField
24498  * 
24499  */
24500
24501 /**
24502  * @class Roo.bootstrap.form.MonthField
24503  * @extends Roo.bootstrap.form.Input
24504  * Bootstrap MonthField class
24505  * 
24506  * @cfg {String} language default en
24507  * 
24508  * @constructor
24509  * Create a new MonthField
24510  * @param {Object} config The config object
24511  */
24512
24513 Roo.bootstrap.form.MonthField = function(config){
24514     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24515     
24516     this.addEvents({
24517         /**
24518          * @event show
24519          * Fires when this field show.
24520          * @param {Roo.bootstrap.form.MonthField} this
24521          * @param {Mixed} date The date value
24522          */
24523         show : true,
24524         /**
24525          * @event show
24526          * Fires when this field hide.
24527          * @param {Roo.bootstrap.form.MonthField} this
24528          * @param {Mixed} date The date value
24529          */
24530         hide : true,
24531         /**
24532          * @event select
24533          * Fires when select a date.
24534          * @param {Roo.bootstrap.form.MonthField} this
24535          * @param {String} oldvalue The old value
24536          * @param {String} newvalue The new value
24537          */
24538         select : true
24539     });
24540 };
24541
24542 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24543     
24544     onRender: function(ct, position)
24545     {
24546         
24547         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24548         
24549         this.language = this.language || 'en';
24550         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24551         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24552         
24553         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24554         this.isInline = false;
24555         this.isInput = true;
24556         this.component = this.el.select('.add-on', true).first() || false;
24557         this.component = (this.component && this.component.length === 0) ? false : this.component;
24558         this.hasInput = this.component && this.inputEL().length;
24559         
24560         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24561         
24562         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24563         
24564         this.picker().on('mousedown', this.onMousedown, this);
24565         this.picker().on('click', this.onClick, this);
24566         
24567         this.picker().addClass('datepicker-dropdown');
24568         
24569         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24570             v.setStyle('width', '189px');
24571         });
24572         
24573         this.fillMonths();
24574         
24575         this.update();
24576         
24577         if(this.isInline) {
24578             this.show();
24579         }
24580         
24581     },
24582     
24583     setValue: function(v, suppressEvent)
24584     {   
24585         var o = this.getValue();
24586         
24587         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24588         
24589         this.update();
24590
24591         if(suppressEvent !== true){
24592             this.fireEvent('select', this, o, v);
24593         }
24594         
24595     },
24596     
24597     getValue: function()
24598     {
24599         return this.value;
24600     },
24601     
24602     onClick: function(e) 
24603     {
24604         e.stopPropagation();
24605         e.preventDefault();
24606         
24607         var target = e.getTarget();
24608         
24609         if(target.nodeName.toLowerCase() === 'i'){
24610             target = Roo.get(target).dom.parentNode;
24611         }
24612         
24613         var nodeName = target.nodeName;
24614         var className = target.className;
24615         var html = target.innerHTML;
24616         
24617         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24618             return;
24619         }
24620         
24621         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24622         
24623         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24624         
24625         this.hide();
24626                         
24627     },
24628     
24629     picker : function()
24630     {
24631         return this.pickerEl;
24632     },
24633     
24634     fillMonths: function()
24635     {    
24636         var i = 0;
24637         var months = this.picker().select('>.datepicker-months td', true).first();
24638         
24639         months.dom.innerHTML = '';
24640         
24641         while (i < 12) {
24642             var month = {
24643                 tag: 'span',
24644                 cls: 'month',
24645                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24646             };
24647             
24648             months.createChild(month);
24649         }
24650         
24651     },
24652     
24653     update: function()
24654     {
24655         var _this = this;
24656         
24657         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24658             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24659         }
24660         
24661         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24662             e.removeClass('active');
24663             
24664             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24665                 e.addClass('active');
24666             }
24667         })
24668     },
24669     
24670     place: function()
24671     {
24672         if(this.isInline) {
24673             return;
24674         }
24675         
24676         this.picker().removeClass(['bottom', 'top']);
24677         
24678         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24679             /*
24680              * place to the top of element!
24681              *
24682              */
24683             
24684             this.picker().addClass('top');
24685             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24686             
24687             return;
24688         }
24689         
24690         this.picker().addClass('bottom');
24691         
24692         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24693     },
24694     
24695     onFocus : function()
24696     {
24697         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24698         this.show();
24699     },
24700     
24701     onBlur : function()
24702     {
24703         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24704         
24705         var d = this.inputEl().getValue();
24706         
24707         this.setValue(d);
24708                 
24709         this.hide();
24710     },
24711     
24712     show : function()
24713     {
24714         this.picker().show();
24715         this.picker().select('>.datepicker-months', true).first().show();
24716         this.update();
24717         this.place();
24718         
24719         this.fireEvent('show', this, this.date);
24720     },
24721     
24722     hide : function()
24723     {
24724         if(this.isInline) {
24725             return;
24726         }
24727         this.picker().hide();
24728         this.fireEvent('hide', this, this.date);
24729         
24730     },
24731     
24732     onMousedown: function(e)
24733     {
24734         e.stopPropagation();
24735         e.preventDefault();
24736     },
24737     
24738     keyup: function(e)
24739     {
24740         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24741         this.update();
24742     },
24743
24744     fireKey: function(e)
24745     {
24746         if (!this.picker().isVisible()){
24747             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24748                 this.show();
24749             }
24750             return;
24751         }
24752         
24753         var dir;
24754         
24755         switch(e.keyCode){
24756             case 27: // escape
24757                 this.hide();
24758                 e.preventDefault();
24759                 break;
24760             case 37: // left
24761             case 39: // right
24762                 dir = e.keyCode == 37 ? -1 : 1;
24763                 
24764                 this.vIndex = this.vIndex + dir;
24765                 
24766                 if(this.vIndex < 0){
24767                     this.vIndex = 0;
24768                 }
24769                 
24770                 if(this.vIndex > 11){
24771                     this.vIndex = 11;
24772                 }
24773                 
24774                 if(isNaN(this.vIndex)){
24775                     this.vIndex = 0;
24776                 }
24777                 
24778                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24779                 
24780                 break;
24781             case 38: // up
24782             case 40: // down
24783                 
24784                 dir = e.keyCode == 38 ? -1 : 1;
24785                 
24786                 this.vIndex = this.vIndex + dir * 4;
24787                 
24788                 if(this.vIndex < 0){
24789                     this.vIndex = 0;
24790                 }
24791                 
24792                 if(this.vIndex > 11){
24793                     this.vIndex = 11;
24794                 }
24795                 
24796                 if(isNaN(this.vIndex)){
24797                     this.vIndex = 0;
24798                 }
24799                 
24800                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24801                 break;
24802                 
24803             case 13: // enter
24804                 
24805                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24806                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24807                 }
24808                 
24809                 this.hide();
24810                 e.preventDefault();
24811                 break;
24812             case 9: // tab
24813                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24814                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24815                 }
24816                 this.hide();
24817                 break;
24818             case 16: // shift
24819             case 17: // ctrl
24820             case 18: // alt
24821                 break;
24822             default :
24823                 this.hide();
24824                 
24825         }
24826     },
24827     
24828     remove: function() 
24829     {
24830         this.picker().remove();
24831     }
24832    
24833 });
24834
24835 Roo.apply(Roo.bootstrap.form.MonthField,  {
24836     
24837     content : {
24838         tag: 'tbody',
24839         cn: [
24840         {
24841             tag: 'tr',
24842             cn: [
24843             {
24844                 tag: 'td',
24845                 colspan: '7'
24846             }
24847             ]
24848         }
24849         ]
24850     },
24851     
24852     dates:{
24853         en: {
24854             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24855             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24856         }
24857     }
24858 });
24859
24860 Roo.apply(Roo.bootstrap.form.MonthField,  {
24861   
24862     template : {
24863         tag: 'div',
24864         cls: 'datepicker dropdown-menu roo-dynamic',
24865         cn: [
24866             {
24867                 tag: 'div',
24868                 cls: 'datepicker-months',
24869                 cn: [
24870                 {
24871                     tag: 'table',
24872                     cls: 'table-condensed',
24873                     cn:[
24874                         Roo.bootstrap.form.DateField.content
24875                     ]
24876                 }
24877                 ]
24878             }
24879         ]
24880     }
24881 });
24882
24883  
24884
24885  
24886  /*
24887  * - LGPL
24888  *
24889  * CheckBox
24890  * 
24891  */
24892
24893 /**
24894  * @class Roo.bootstrap.form.CheckBox
24895  * @extends Roo.bootstrap.form.Input
24896  * Bootstrap CheckBox class
24897  * 
24898  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24899  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24900  * @cfg {String} boxLabel The text that appears beside the checkbox
24901  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24902  * @cfg {Boolean} checked initnal the element
24903  * @cfg {Boolean} inline inline the element (default false)
24904  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24905  * @cfg {String} tooltip label tooltip
24906  * 
24907  * @constructor
24908  * Create a new CheckBox
24909  * @param {Object} config The config object
24910  */
24911
24912 Roo.bootstrap.form.CheckBox = function(config){
24913     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24914    
24915     this.addEvents({
24916         /**
24917         * @event check
24918         * Fires when the element is checked or unchecked.
24919         * @param {Roo.bootstrap.form.CheckBox} this This input
24920         * @param {Boolean} checked The new checked value
24921         */
24922        check : true,
24923        /**
24924         * @event click
24925         * Fires when the element is click.
24926         * @param {Roo.bootstrap.form.CheckBox} this This input
24927         */
24928        click : true
24929     });
24930     
24931 };
24932
24933 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24934   
24935     inputType: 'checkbox',
24936     inputValue: 1,
24937     valueOff: 0,
24938     boxLabel: false,
24939     checked: false,
24940     weight : false,
24941     inline: false,
24942     tooltip : '',
24943     
24944     // checkbox success does not make any sense really.. 
24945     invalidClass : "",
24946     validClass : "",
24947     
24948     
24949     getAutoCreate : function()
24950     {
24951         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24952         
24953         var id = Roo.id();
24954         
24955         var cfg = {};
24956         
24957         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24958         
24959         if(this.inline){
24960             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24961         }
24962         
24963         var input =  {
24964             tag: 'input',
24965             id : id,
24966             type : this.inputType,
24967             value : this.inputValue,
24968             cls : 'roo-' + this.inputType, //'form-box',
24969             placeholder : this.placeholder || ''
24970             
24971         };
24972         
24973         if(this.inputType != 'radio'){
24974             var hidden =  {
24975                 tag: 'input',
24976                 type : 'hidden',
24977                 cls : 'roo-hidden-value',
24978                 value : this.checked ? this.inputValue : this.valueOff
24979             };
24980         }
24981         
24982             
24983         if (this.weight) { // Validity check?
24984             cfg.cls += " " + this.inputType + "-" + this.weight;
24985         }
24986         
24987         if (this.disabled) {
24988             input.disabled=true;
24989         }
24990         
24991         if(this.checked){
24992             input.checked = this.checked;
24993         }
24994         
24995         if (this.name) {
24996             
24997             input.name = this.name;
24998             
24999             if(this.inputType != 'radio'){
25000                 hidden.name = this.name;
25001                 input.name = '_hidden_' + this.name;
25002             }
25003         }
25004         
25005         if (this.size) {
25006             input.cls += ' input-' + this.size;
25007         }
25008         
25009         var settings=this;
25010         
25011         ['xs','sm','md','lg'].map(function(size){
25012             if (settings[size]) {
25013                 cfg.cls += ' col-' + size + '-' + settings[size];
25014             }
25015         });
25016         
25017         var inputblock = input;
25018          
25019         if (this.before || this.after) {
25020             
25021             inputblock = {
25022                 cls : 'input-group',
25023                 cn :  [] 
25024             };
25025             
25026             if (this.before) {
25027                 inputblock.cn.push({
25028                     tag :'span',
25029                     cls : 'input-group-addon',
25030                     html : this.before
25031                 });
25032             }
25033             
25034             inputblock.cn.push(input);
25035             
25036             if(this.inputType != 'radio'){
25037                 inputblock.cn.push(hidden);
25038             }
25039             
25040             if (this.after) {
25041                 inputblock.cn.push({
25042                     tag :'span',
25043                     cls : 'input-group-addon',
25044                     html : this.after
25045                 });
25046             }
25047             
25048         }
25049         var boxLabelCfg = false;
25050         
25051         if(this.boxLabel){
25052            
25053             boxLabelCfg = {
25054                 tag: 'label',
25055                 //'for': id, // box label is handled by onclick - so no for...
25056                 cls: 'box-label',
25057                 html: this.boxLabel
25058             };
25059             if(this.tooltip){
25060                 boxLabelCfg.tooltip = this.tooltip;
25061             }
25062              
25063         }
25064         
25065         
25066         if (align ==='left' && this.fieldLabel.length) {
25067 //                Roo.log("left and has label");
25068             cfg.cn = [
25069                 {
25070                     tag: 'label',
25071                     'for' :  id,
25072                     cls : 'control-label',
25073                     html : this.fieldLabel
25074                 },
25075                 {
25076                     cls : "", 
25077                     cn: [
25078                         inputblock
25079                     ]
25080                 }
25081             ];
25082             
25083             if (boxLabelCfg) {
25084                 cfg.cn[1].cn.push(boxLabelCfg);
25085             }
25086             
25087             if(this.labelWidth > 12){
25088                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25089             }
25090             
25091             if(this.labelWidth < 13 && this.labelmd == 0){
25092                 this.labelmd = this.labelWidth;
25093             }
25094             
25095             if(this.labellg > 0){
25096                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25097                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25098             }
25099             
25100             if(this.labelmd > 0){
25101                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25102                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25103             }
25104             
25105             if(this.labelsm > 0){
25106                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25107                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25108             }
25109             
25110             if(this.labelxs > 0){
25111                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25112                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25113             }
25114             
25115         } else if ( this.fieldLabel.length) {
25116 //                Roo.log(" label");
25117                 cfg.cn = [
25118                    
25119                     {
25120                         tag: this.boxLabel ? 'span' : 'label',
25121                         'for': id,
25122                         cls: 'control-label box-input-label',
25123                         //cls : 'input-group-addon',
25124                         html : this.fieldLabel
25125                     },
25126                     
25127                     inputblock
25128                     
25129                 ];
25130                 if (boxLabelCfg) {
25131                     cfg.cn.push(boxLabelCfg);
25132                 }
25133
25134         } else {
25135             
25136 //                Roo.log(" no label && no align");
25137                 cfg.cn = [  inputblock ] ;
25138                 if (boxLabelCfg) {
25139                     cfg.cn.push(boxLabelCfg);
25140                 }
25141
25142                 
25143         }
25144         
25145        
25146         
25147         if(this.inputType != 'radio'){
25148             cfg.cn.push(hidden);
25149         }
25150         
25151         return cfg;
25152         
25153     },
25154     
25155     /**
25156      * return the real input element.
25157      */
25158     inputEl: function ()
25159     {
25160         return this.el.select('input.roo-' + this.inputType,true).first();
25161     },
25162     hiddenEl: function ()
25163     {
25164         return this.el.select('input.roo-hidden-value',true).first();
25165     },
25166     
25167     labelEl: function()
25168     {
25169         return this.el.select('label.control-label',true).first();
25170     },
25171     /* depricated... */
25172     
25173     label: function()
25174     {
25175         return this.labelEl();
25176     },
25177     
25178     boxLabelEl: function()
25179     {
25180         return this.el.select('label.box-label',true).first();
25181     },
25182     
25183     initEvents : function()
25184     {
25185 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25186         
25187         this.inputEl().on('click', this.onClick,  this);
25188         
25189         if (this.boxLabel) { 
25190             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25191         }
25192         
25193         this.startValue = this.getValue();
25194         
25195         if(this.groupId){
25196             Roo.bootstrap.form.CheckBox.register(this);
25197         }
25198     },
25199     
25200     onClick : function(e)
25201     {   
25202         if(this.fireEvent('click', this, e) !== false){
25203             this.setChecked(!this.checked);
25204         }
25205         
25206     },
25207     
25208     setChecked : function(state,suppressEvent)
25209     {
25210         this.startValue = this.getValue();
25211
25212         if(this.inputType == 'radio'){
25213             
25214             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25215                 e.dom.checked = false;
25216             });
25217             
25218             this.inputEl().dom.checked = true;
25219             
25220             this.inputEl().dom.value = this.inputValue;
25221             
25222             if(suppressEvent !== true){
25223                 this.fireEvent('check', this, true);
25224             }
25225             
25226             this.validate();
25227             
25228             return;
25229         }
25230         
25231         this.checked = state;
25232         
25233         this.inputEl().dom.checked = state;
25234         
25235         
25236         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25237         
25238         if(suppressEvent !== true){
25239             this.fireEvent('check', this, state);
25240         }
25241         
25242         this.validate();
25243     },
25244     
25245     getValue : function()
25246     {
25247         if(this.inputType == 'radio'){
25248             return this.getGroupValue();
25249         }
25250         
25251         return this.hiddenEl().dom.value;
25252         
25253     },
25254     
25255     getGroupValue : function()
25256     {
25257         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25258             return '';
25259         }
25260         
25261         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25262     },
25263     
25264     setValue : function(v,suppressEvent)
25265     {
25266         if(this.inputType == 'radio'){
25267             this.setGroupValue(v, suppressEvent);
25268             return;
25269         }
25270         
25271         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25272         
25273         this.validate();
25274     },
25275     
25276     setGroupValue : function(v, suppressEvent)
25277     {
25278         this.startValue = this.getValue();
25279         
25280         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25281             e.dom.checked = false;
25282             
25283             if(e.dom.value == v){
25284                 e.dom.checked = true;
25285             }
25286         });
25287         
25288         if(suppressEvent !== true){
25289             this.fireEvent('check', this, true);
25290         }
25291
25292         this.validate();
25293         
25294         return;
25295     },
25296     
25297     validate : function()
25298     {
25299         if(this.getVisibilityEl().hasClass('hidden')){
25300             return true;
25301         }
25302         
25303         if(
25304                 this.disabled || 
25305                 (this.inputType == 'radio' && this.validateRadio()) ||
25306                 (this.inputType == 'checkbox' && this.validateCheckbox())
25307         ){
25308             this.markValid();
25309             return true;
25310         }
25311         
25312         this.markInvalid();
25313         return false;
25314     },
25315     
25316     validateRadio : function()
25317     {
25318         if(this.getVisibilityEl().hasClass('hidden')){
25319             return true;
25320         }
25321         
25322         if(this.allowBlank){
25323             return true;
25324         }
25325         
25326         var valid = false;
25327         
25328         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25329             if(!e.dom.checked){
25330                 return;
25331             }
25332             
25333             valid = true;
25334             
25335             return false;
25336         });
25337         
25338         return valid;
25339     },
25340     
25341     validateCheckbox : function()
25342     {
25343         if(!this.groupId){
25344             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25345             //return (this.getValue() == this.inputValue) ? true : false;
25346         }
25347         
25348         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25349         
25350         if(!group){
25351             return false;
25352         }
25353         
25354         var r = false;
25355         
25356         for(var i in group){
25357             if(group[i].el.isVisible(true)){
25358                 r = false;
25359                 break;
25360             }
25361             
25362             r = true;
25363         }
25364         
25365         for(var i in group){
25366             if(r){
25367                 break;
25368             }
25369             
25370             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25371         }
25372         
25373         return r;
25374     },
25375     
25376     /**
25377      * Mark this field as valid
25378      */
25379     markValid : function()
25380     {
25381         var _this = this;
25382         
25383         this.fireEvent('valid', this);
25384         
25385         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25386         
25387         if(this.groupId){
25388             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25389         }
25390         
25391         if(label){
25392             label.markValid();
25393         }
25394
25395         if(this.inputType == 'radio'){
25396             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25397                 var fg = e.findParent('.form-group', false, true);
25398                 if (Roo.bootstrap.version == 3) {
25399                     fg.removeClass([_this.invalidClass, _this.validClass]);
25400                     fg.addClass(_this.validClass);
25401                 } else {
25402                     fg.removeClass(['is-valid', 'is-invalid']);
25403                     fg.addClass('is-valid');
25404                 }
25405             });
25406             
25407             return;
25408         }
25409
25410         if(!this.groupId){
25411             var fg = this.el.findParent('.form-group', false, true);
25412             if (Roo.bootstrap.version == 3) {
25413                 fg.removeClass([this.invalidClass, this.validClass]);
25414                 fg.addClass(this.validClass);
25415             } else {
25416                 fg.removeClass(['is-valid', 'is-invalid']);
25417                 fg.addClass('is-valid');
25418             }
25419             return;
25420         }
25421         
25422         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25423         
25424         if(!group){
25425             return;
25426         }
25427         
25428         for(var i in group){
25429             var fg = group[i].el.findParent('.form-group', false, true);
25430             if (Roo.bootstrap.version == 3) {
25431                 fg.removeClass([this.invalidClass, this.validClass]);
25432                 fg.addClass(this.validClass);
25433             } else {
25434                 fg.removeClass(['is-valid', 'is-invalid']);
25435                 fg.addClass('is-valid');
25436             }
25437         }
25438     },
25439     
25440      /**
25441      * Mark this field as invalid
25442      * @param {String} msg The validation message
25443      */
25444     markInvalid : function(msg)
25445     {
25446         if(this.allowBlank){
25447             return;
25448         }
25449         
25450         var _this = this;
25451         
25452         this.fireEvent('invalid', this, msg);
25453         
25454         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25455         
25456         if(this.groupId){
25457             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25458         }
25459         
25460         if(label){
25461             label.markInvalid();
25462         }
25463             
25464         if(this.inputType == 'radio'){
25465             
25466             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25467                 var fg = e.findParent('.form-group', false, true);
25468                 if (Roo.bootstrap.version == 3) {
25469                     fg.removeClass([_this.invalidClass, _this.validClass]);
25470                     fg.addClass(_this.invalidClass);
25471                 } else {
25472                     fg.removeClass(['is-invalid', 'is-valid']);
25473                     fg.addClass('is-invalid');
25474                 }
25475             });
25476             
25477             return;
25478         }
25479         
25480         if(!this.groupId){
25481             var fg = this.el.findParent('.form-group', false, true);
25482             if (Roo.bootstrap.version == 3) {
25483                 fg.removeClass([_this.invalidClass, _this.validClass]);
25484                 fg.addClass(_this.invalidClass);
25485             } else {
25486                 fg.removeClass(['is-invalid', 'is-valid']);
25487                 fg.addClass('is-invalid');
25488             }
25489             return;
25490         }
25491         
25492         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25493         
25494         if(!group){
25495             return;
25496         }
25497         
25498         for(var i in group){
25499             var fg = group[i].el.findParent('.form-group', false, true);
25500             if (Roo.bootstrap.version == 3) {
25501                 fg.removeClass([_this.invalidClass, _this.validClass]);
25502                 fg.addClass(_this.invalidClass);
25503             } else {
25504                 fg.removeClass(['is-invalid', 'is-valid']);
25505                 fg.addClass('is-invalid');
25506             }
25507         }
25508         
25509     },
25510     
25511     clearInvalid : function()
25512     {
25513         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25514         
25515         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25516         
25517         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25518         
25519         if (label && label.iconEl) {
25520             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25521             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25522         }
25523     },
25524     
25525     disable : function()
25526     {
25527         if(this.inputType != 'radio'){
25528             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25529             return;
25530         }
25531         
25532         var _this = this;
25533         
25534         if(this.rendered){
25535             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25536                 _this.getActionEl().addClass(this.disabledClass);
25537                 e.dom.disabled = true;
25538             });
25539         }
25540         
25541         this.disabled = true;
25542         this.fireEvent("disable", this);
25543         return this;
25544     },
25545
25546     enable : function()
25547     {
25548         if(this.inputType != 'radio'){
25549             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25550             return;
25551         }
25552         
25553         var _this = this;
25554         
25555         if(this.rendered){
25556             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25557                 _this.getActionEl().removeClass(this.disabledClass);
25558                 e.dom.disabled = false;
25559             });
25560         }
25561         
25562         this.disabled = false;
25563         this.fireEvent("enable", this);
25564         return this;
25565     },
25566     
25567     setBoxLabel : function(v)
25568     {
25569         this.boxLabel = v;
25570         
25571         if(this.rendered){
25572             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25573         }
25574     }
25575
25576 });
25577
25578 Roo.apply(Roo.bootstrap.form.CheckBox, {
25579     
25580     groups: {},
25581     
25582      /**
25583     * register a CheckBox Group
25584     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25585     */
25586     register : function(checkbox)
25587     {
25588         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25589             this.groups[checkbox.groupId] = {};
25590         }
25591         
25592         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25593             return;
25594         }
25595         
25596         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25597         
25598     },
25599     /**
25600     * fetch a CheckBox Group based on the group ID
25601     * @param {string} the group ID
25602     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25603     */
25604     get: function(groupId) {
25605         if (typeof(this.groups[groupId]) == 'undefined') {
25606             return false;
25607         }
25608         
25609         return this.groups[groupId] ;
25610     }
25611     
25612     
25613 });
25614 /*
25615  * - LGPL
25616  *
25617  * RadioItem
25618  * 
25619  */
25620
25621 /**
25622  * @class Roo.bootstrap.form.Radio
25623  * @extends Roo.bootstrap.Component
25624  * Bootstrap Radio class
25625  * @cfg {String} boxLabel - the label associated
25626  * @cfg {String} value - the value of radio
25627  * 
25628  * @constructor
25629  * Create a new Radio
25630  * @param {Object} config The config object
25631  */
25632 Roo.bootstrap.form.Radio = function(config){
25633     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25634     
25635 };
25636
25637 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25638     
25639     boxLabel : '',
25640     
25641     value : '',
25642     
25643     getAutoCreate : function()
25644     {
25645         var cfg = {
25646             tag : 'div',
25647             cls : 'form-group radio',
25648             cn : [
25649                 {
25650                     tag : 'label',
25651                     cls : 'box-label',
25652                     html : this.boxLabel
25653                 }
25654             ]
25655         };
25656         
25657         return cfg;
25658     },
25659     
25660     initEvents : function() 
25661     {
25662         this.parent().register(this);
25663         
25664         this.el.on('click', this.onClick, this);
25665         
25666     },
25667     
25668     onClick : function(e)
25669     {
25670         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25671             this.setChecked(true);
25672         }
25673     },
25674     
25675     setChecked : function(state, suppressEvent)
25676     {
25677         this.parent().setValue(this.value, suppressEvent);
25678         
25679     },
25680     
25681     setBoxLabel : function(v)
25682     {
25683         this.boxLabel = v;
25684         
25685         if(this.rendered){
25686             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25687         }
25688     }
25689     
25690 });
25691  
25692
25693  /*
25694  * - LGPL
25695  *
25696  * Input
25697  * 
25698  */
25699
25700 /**
25701  * @class Roo.bootstrap.form.SecurePass
25702  * @extends Roo.bootstrap.form.Input
25703  * Bootstrap SecurePass class
25704  *
25705  * 
25706  * @constructor
25707  * Create a new SecurePass
25708  * @param {Object} config The config object
25709  */
25710  
25711 Roo.bootstrap.form.SecurePass = function (config) {
25712     // these go here, so the translation tool can replace them..
25713     this.errors = {
25714         PwdEmpty: "Please type a password, and then retype it to confirm.",
25715         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25716         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25717         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25718         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25719         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25720         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25721         TooWeak: "Your password is Too Weak."
25722     },
25723     this.meterLabel = "Password strength:";
25724     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25725     this.meterClass = [
25726         "roo-password-meter-tooweak", 
25727         "roo-password-meter-weak", 
25728         "roo-password-meter-medium", 
25729         "roo-password-meter-strong", 
25730         "roo-password-meter-grey"
25731     ];
25732     
25733     this.errors = {};
25734     
25735     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25736 }
25737
25738 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25739     /**
25740      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25741      * {
25742      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25743      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25744      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25745      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25746      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25747      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25748      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25749      * })
25750      */
25751     // private
25752     
25753     meterWidth: 300,
25754     errorMsg :'',    
25755     errors: false,
25756     imageRoot: '/',
25757     /**
25758      * @cfg {String/Object} Label for the strength meter (defaults to
25759      * 'Password strength:')
25760      */
25761     // private
25762     meterLabel: '',
25763     /**
25764      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25765      * ['Weak', 'Medium', 'Strong'])
25766      */
25767     // private    
25768     pwdStrengths: false,    
25769     // private
25770     strength: 0,
25771     // private
25772     _lastPwd: null,
25773     // private
25774     kCapitalLetter: 0,
25775     kSmallLetter: 1,
25776     kDigit: 2,
25777     kPunctuation: 3,
25778     
25779     insecure: false,
25780     // private
25781     initEvents: function ()
25782     {
25783         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25784
25785         if (this.el.is('input[type=password]') && Roo.isSafari) {
25786             this.el.on('keydown', this.SafariOnKeyDown, this);
25787         }
25788
25789         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25790     },
25791     // private
25792     onRender: function (ct, position)
25793     {
25794         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25795         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25796         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25797
25798         this.trigger.createChild({
25799                    cn: [
25800                     {
25801                     //id: 'PwdMeter',
25802                     tag: 'div',
25803                     cls: 'roo-password-meter-grey col-xs-12',
25804                     style: {
25805                         //width: 0,
25806                         //width: this.meterWidth + 'px'                                                
25807                         }
25808                     },
25809                     {                            
25810                          cls: 'roo-password-meter-text'                          
25811                     }
25812                 ]            
25813         });
25814
25815          
25816         if (this.hideTrigger) {
25817             this.trigger.setDisplayed(false);
25818         }
25819         this.setSize(this.width || '', this.height || '');
25820     },
25821     // private
25822     onDestroy: function ()
25823     {
25824         if (this.trigger) {
25825             this.trigger.removeAllListeners();
25826             this.trigger.remove();
25827         }
25828         if (this.wrap) {
25829             this.wrap.remove();
25830         }
25831         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25832     },
25833     // private
25834     checkStrength: function ()
25835     {
25836         var pwd = this.inputEl().getValue();
25837         if (pwd == this._lastPwd) {
25838             return;
25839         }
25840
25841         var strength;
25842         if (this.ClientSideStrongPassword(pwd)) {
25843             strength = 3;
25844         } else if (this.ClientSideMediumPassword(pwd)) {
25845             strength = 2;
25846         } else if (this.ClientSideWeakPassword(pwd)) {
25847             strength = 1;
25848         } else {
25849             strength = 0;
25850         }
25851         
25852         Roo.log('strength1: ' + strength);
25853         
25854         //var pm = this.trigger.child('div/div/div').dom;
25855         var pm = this.trigger.child('div/div');
25856         pm.removeClass(this.meterClass);
25857         pm.addClass(this.meterClass[strength]);
25858                 
25859         
25860         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25861                 
25862         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25863         
25864         this._lastPwd = pwd;
25865     },
25866     reset: function ()
25867     {
25868         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25869         
25870         this._lastPwd = '';
25871         
25872         var pm = this.trigger.child('div/div');
25873         pm.removeClass(this.meterClass);
25874         pm.addClass('roo-password-meter-grey');        
25875         
25876         
25877         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25878         
25879         pt.innerHTML = '';
25880         this.inputEl().dom.type='password';
25881     },
25882     // private
25883     validateValue: function (value)
25884     {
25885         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25886             return false;
25887         }
25888         if (value.length == 0) {
25889             if (this.allowBlank) {
25890                 this.clearInvalid();
25891                 return true;
25892             }
25893
25894             this.markInvalid(this.errors.PwdEmpty);
25895             this.errorMsg = this.errors.PwdEmpty;
25896             return false;
25897         }
25898         
25899         if(this.insecure){
25900             return true;
25901         }
25902         
25903         if (!value.match(/[\x21-\x7e]+/)) {
25904             this.markInvalid(this.errors.PwdBadChar);
25905             this.errorMsg = this.errors.PwdBadChar;
25906             return false;
25907         }
25908         if (value.length < 6) {
25909             this.markInvalid(this.errors.PwdShort);
25910             this.errorMsg = this.errors.PwdShort;
25911             return false;
25912         }
25913         if (value.length > 16) {
25914             this.markInvalid(this.errors.PwdLong);
25915             this.errorMsg = this.errors.PwdLong;
25916             return false;
25917         }
25918         var strength;
25919         if (this.ClientSideStrongPassword(value)) {
25920             strength = 3;
25921         } else if (this.ClientSideMediumPassword(value)) {
25922             strength = 2;
25923         } else if (this.ClientSideWeakPassword(value)) {
25924             strength = 1;
25925         } else {
25926             strength = 0;
25927         }
25928
25929         
25930         if (strength < 2) {
25931             //this.markInvalid(this.errors.TooWeak);
25932             this.errorMsg = this.errors.TooWeak;
25933             //return false;
25934         }
25935         
25936         
25937         console.log('strength2: ' + strength);
25938         
25939         //var pm = this.trigger.child('div/div/div').dom;
25940         
25941         var pm = this.trigger.child('div/div');
25942         pm.removeClass(this.meterClass);
25943         pm.addClass(this.meterClass[strength]);
25944                 
25945         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25946                 
25947         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25948         
25949         this.errorMsg = ''; 
25950         return true;
25951     },
25952     // private
25953     CharacterSetChecks: function (type)
25954     {
25955         this.type = type;
25956         this.fResult = false;
25957     },
25958     // private
25959     isctype: function (character, type)
25960     {
25961         switch (type) {  
25962             case this.kCapitalLetter:
25963                 if (character >= 'A' && character <= 'Z') {
25964                     return true;
25965                 }
25966                 break;
25967             
25968             case this.kSmallLetter:
25969                 if (character >= 'a' && character <= 'z') {
25970                     return true;
25971                 }
25972                 break;
25973             
25974             case this.kDigit:
25975                 if (character >= '0' && character <= '9') {
25976                     return true;
25977                 }
25978                 break;
25979             
25980             case this.kPunctuation:
25981                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25982                     return true;
25983                 }
25984                 break;
25985             
25986             default:
25987                 return false;
25988         }
25989
25990     },
25991     // private
25992     IsLongEnough: function (pwd, size)
25993     {
25994         return !(pwd == null || isNaN(size) || pwd.length < size);
25995     },
25996     // private
25997     SpansEnoughCharacterSets: function (word, nb)
25998     {
25999         if (!this.IsLongEnough(word, nb))
26000         {
26001             return false;
26002         }
26003
26004         var characterSetChecks = new Array(
26005             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26006             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26007         );
26008         
26009         for (var index = 0; index < word.length; ++index) {
26010             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26011                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26012                     characterSetChecks[nCharSet].fResult = true;
26013                     break;
26014                 }
26015             }
26016         }
26017
26018         var nCharSets = 0;
26019         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26020             if (characterSetChecks[nCharSet].fResult) {
26021                 ++nCharSets;
26022             }
26023         }
26024
26025         if (nCharSets < nb) {
26026             return false;
26027         }
26028         return true;
26029     },
26030     // private
26031     ClientSideStrongPassword: function (pwd)
26032     {
26033         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26034     },
26035     // private
26036     ClientSideMediumPassword: function (pwd)
26037     {
26038         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26039     },
26040     // private
26041     ClientSideWeakPassword: function (pwd)
26042     {
26043         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26044     }
26045           
26046 });
26047 Roo.htmleditor = {};
26048  
26049 /**
26050  * @class Roo.htmleditor.Filter
26051  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26052  * @cfg {DomElement} node The node to iterate and filter
26053  * @cfg {boolean|String|Array} tag Tags to replace 
26054  * @constructor
26055  * Create a new Filter.
26056  * @param {Object} config Configuration options
26057  */
26058
26059
26060
26061 Roo.htmleditor.Filter = function(cfg) {
26062     Roo.apply(this.cfg);
26063     // this does not actually call walk as it's really just a abstract class
26064 }
26065
26066
26067 Roo.htmleditor.Filter.prototype = {
26068     
26069     node: false,
26070     
26071     tag: false,
26072
26073     // overrride to do replace comments.
26074     replaceComment : false,
26075     
26076     // overrride to do replace or do stuff with tags..
26077     replaceTag : false,
26078     
26079     walk : function(dom)
26080     {
26081         Roo.each( Array.from(dom.childNodes), function( e ) {
26082             switch(true) {
26083                 
26084                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26085                     this.replaceComment(e);
26086                     return;
26087                 
26088                 case e.nodeType != 1: //not a node.
26089                     return;
26090                 
26091                 case this.tag === true: // everything
26092                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26093                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26094                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26095                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26096                     if (this.replaceTag && false === this.replaceTag(e)) {
26097                         return;
26098                     }
26099                     if (e.hasChildNodes()) {
26100                         this.walk(e);
26101                     }
26102                     return;
26103                 
26104                 default:    // tags .. that do not match.
26105                     if (e.hasChildNodes()) {
26106                         this.walk(e);
26107                     }
26108             }
26109             
26110         }, this);
26111         
26112     },
26113     
26114     
26115     removeNodeKeepChildren : function( node)
26116     {
26117     
26118         ar = Array.from(node.childNodes);
26119         for (var i = 0; i < ar.length; i++) {
26120          
26121             node.removeChild(ar[i]);
26122             // what if we need to walk these???
26123             node.parentNode.insertBefore(ar[i], node);
26124            
26125         }
26126         node.parentNode.removeChild(node);
26127     }
26128 }; 
26129
26130 /**
26131  * @class Roo.htmleditor.FilterAttributes
26132  * clean attributes and  styles including http:// etc.. in attribute
26133  * @constructor
26134 * Run a new Attribute Filter
26135 * @param {Object} config Configuration options
26136  */
26137 Roo.htmleditor.FilterAttributes = function(cfg)
26138 {
26139     Roo.apply(this, cfg);
26140     this.attrib_black = this.attrib_black || [];
26141     this.attrib_white = this.attrib_white || [];
26142
26143     this.attrib_clean = this.attrib_clean || [];
26144     this.style_white = this.style_white || [];
26145     this.style_black = this.style_black || [];
26146     this.walk(cfg.node);
26147 }
26148
26149 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26150 {
26151     tag: true, // all tags
26152     
26153     attrib_black : false, // array
26154     attrib_clean : false,
26155     attrib_white : false,
26156
26157     style_white : false,
26158     style_black : false,
26159      
26160      
26161     replaceTag : function(node)
26162     {
26163         if (!node.attributes || !node.attributes.length) {
26164             return true;
26165         }
26166         
26167         for (var i = node.attributes.length-1; i > -1 ; i--) {
26168             var a = node.attributes[i];
26169             //console.log(a);
26170             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26171                 node.removeAttribute(a.name);
26172                 continue;
26173             }
26174             
26175             
26176             
26177             if (a.name.toLowerCase().substr(0,2)=='on')  {
26178                 node.removeAttribute(a.name);
26179                 continue;
26180             }
26181             
26182             
26183             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26184                 node.removeAttribute(a.name);
26185                 continue;
26186             }
26187             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26188                 this.cleanAttr(node,a.name,a.value); // fixme..
26189                 continue;
26190             }
26191             if (a.name == 'style') {
26192                 this.cleanStyle(node,a.name,a.value);
26193                 continue;
26194             }
26195             /// clean up MS crap..
26196             // tecnically this should be a list of valid class'es..
26197             
26198             
26199             if (a.name == 'class') {
26200                 if (a.value.match(/^Mso/)) {
26201                     node.removeAttribute('class');
26202                 }
26203                 
26204                 if (a.value.match(/^body$/)) {
26205                     node.removeAttribute('class');
26206                 }
26207                 continue;
26208             }
26209             
26210             
26211             // style cleanup!?
26212             // class cleanup?
26213             
26214         }
26215         return true; // clean children
26216     },
26217         
26218     cleanAttr: function(node, n,v)
26219     {
26220         
26221         if (v.match(/^\./) || v.match(/^\//)) {
26222             return;
26223         }
26224         if (v.match(/^(http|https):\/\//)
26225             || v.match(/^mailto:/) 
26226             || v.match(/^ftp:/)
26227             || v.match(/^data:/)
26228             ) {
26229             return;
26230         }
26231         if (v.match(/^#/)) {
26232             return;
26233         }
26234         if (v.match(/^\{/)) { // allow template editing.
26235             return;
26236         }
26237 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26238         node.removeAttribute(n);
26239         
26240     },
26241     cleanStyle : function(node,  n,v)
26242     {
26243         if (v.match(/expression/)) { //XSS?? should we even bother..
26244             node.removeAttribute(n);
26245             return;
26246         }
26247         
26248         var parts = v.split(/;/);
26249         var clean = [];
26250         
26251         Roo.each(parts, function(p) {
26252             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26253             if (!p.length) {
26254                 return true;
26255             }
26256             var l = p.split(':').shift().replace(/\s+/g,'');
26257             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26258             
26259             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26260                 return true;
26261             }
26262             //Roo.log()
26263             // only allow 'c whitelisted system attributes'
26264             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26265                 return true;
26266             }
26267             
26268             
26269             clean.push(p);
26270             return true;
26271         },this);
26272         if (clean.length) { 
26273             node.setAttribute(n, clean.join(';'));
26274         } else {
26275             node.removeAttribute(n);
26276         }
26277         
26278     }
26279         
26280         
26281         
26282     
26283 });/**
26284  * @class Roo.htmleditor.FilterBlack
26285  * remove blacklisted elements.
26286  * @constructor
26287  * Run a new Blacklisted Filter
26288  * @param {Object} config Configuration options
26289  */
26290
26291 Roo.htmleditor.FilterBlack = function(cfg)
26292 {
26293     Roo.apply(this, cfg);
26294     this.walk(cfg.node);
26295 }
26296
26297 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26298 {
26299     tag : true, // all elements.
26300    
26301     replaceTag : function(n)
26302     {
26303         n.parentNode.removeChild(n);
26304     }
26305 });
26306 /**
26307  * @class Roo.htmleditor.FilterComment
26308  * remove comments.
26309  * @constructor
26310 * Run a new Comments Filter
26311 * @param {Object} config Configuration options
26312  */
26313 Roo.htmleditor.FilterComment = function(cfg)
26314 {
26315     this.walk(cfg.node);
26316 }
26317
26318 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26319 {
26320   
26321     replaceComment : function(n)
26322     {
26323         n.parentNode.removeChild(n);
26324     }
26325 });/**
26326  * @class Roo.htmleditor.FilterKeepChildren
26327  * remove tags but keep children
26328  * @constructor
26329  * Run a new Keep Children Filter
26330  * @param {Object} config Configuration options
26331  */
26332
26333 Roo.htmleditor.FilterKeepChildren = function(cfg)
26334 {
26335     Roo.apply(this, cfg);
26336     if (this.tag === false) {
26337         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26338     }
26339     // hacky?
26340     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26341         this.cleanNamespace = true;
26342     }
26343         
26344     this.walk(cfg.node);
26345 }
26346
26347 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26348 {
26349     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26350   
26351     replaceTag : function(node)
26352     {
26353         // walk children...
26354         //Roo.log(node.tagName);
26355         var ar = Array.from(node.childNodes);
26356         //remove first..
26357         
26358         for (var i = 0; i < ar.length; i++) {
26359             var e = ar[i];
26360             if (e.nodeType == 1) {
26361                 if (
26362                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26363                     || // array and it matches
26364                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26365                     ||
26366                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26367                     ||
26368                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26369                 ) {
26370                     this.replaceTag(ar[i]); // child is blacklisted as well...
26371                     continue;
26372                 }
26373             }
26374         }  
26375         ar = Array.from(node.childNodes);
26376         for (var i = 0; i < ar.length; i++) {
26377          
26378             node.removeChild(ar[i]);
26379             // what if we need to walk these???
26380             node.parentNode.insertBefore(ar[i], node);
26381             if (this.tag !== false) {
26382                 this.walk(ar[i]);
26383                 
26384             }
26385         }
26386         //Roo.log("REMOVE:" + node.tagName);
26387         node.parentNode.removeChild(node);
26388         return false; // don't walk children
26389         
26390         
26391     }
26392 });/**
26393  * @class Roo.htmleditor.FilterParagraph
26394  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26395  * like on 'push' to remove the <p> tags and replace them with line breaks.
26396  * @constructor
26397  * Run a new Paragraph Filter
26398  * @param {Object} config Configuration options
26399  */
26400
26401 Roo.htmleditor.FilterParagraph = function(cfg)
26402 {
26403     // no need to apply config.
26404     this.walk(cfg.node);
26405 }
26406
26407 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26408 {
26409     
26410      
26411     tag : 'P',
26412     
26413      
26414     replaceTag : function(node)
26415     {
26416         
26417         if (node.childNodes.length == 1 &&
26418             node.childNodes[0].nodeType == 3 &&
26419             node.childNodes[0].textContent.trim().length < 1
26420             ) {
26421             // remove and replace with '<BR>';
26422             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26423             return false; // no need to walk..
26424         }
26425         var ar = Array.from(node.childNodes);
26426         for (var i = 0; i < ar.length; i++) {
26427             node.removeChild(ar[i]);
26428             // what if we need to walk these???
26429             node.parentNode.insertBefore(ar[i], node);
26430         }
26431         // now what about this?
26432         // <p> &nbsp; </p>
26433         
26434         // double BR.
26435         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26436         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26437         node.parentNode.removeChild(node);
26438         
26439         return false;
26440
26441     }
26442     
26443 });/**
26444  * @class Roo.htmleditor.FilterSpan
26445  * filter span's with no attributes out..
26446  * @constructor
26447  * Run a new Span Filter
26448  * @param {Object} config Configuration options
26449  */
26450
26451 Roo.htmleditor.FilterSpan = function(cfg)
26452 {
26453     // no need to apply config.
26454     this.walk(cfg.node);
26455 }
26456
26457 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26458 {
26459      
26460     tag : 'SPAN',
26461      
26462  
26463     replaceTag : function(node)
26464     {
26465         if (node.attributes && node.attributes.length > 0) {
26466             return true; // walk if there are any.
26467         }
26468         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26469         return false;
26470      
26471     }
26472     
26473 });/**
26474  * @class Roo.htmleditor.FilterTableWidth
26475   try and remove table width data - as that frequently messes up other stuff.
26476  * 
26477  *      was cleanTableWidths.
26478  *
26479  * Quite often pasting from word etc.. results in tables with column and widths.
26480  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26481  *
26482  * @constructor
26483  * Run a new Table Filter
26484  * @param {Object} config Configuration options
26485  */
26486
26487 Roo.htmleditor.FilterTableWidth = function(cfg)
26488 {
26489     // no need to apply config.
26490     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26491     this.walk(cfg.node);
26492 }
26493
26494 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26495 {
26496      
26497      
26498     
26499     replaceTag: function(node) {
26500         
26501         
26502       
26503         if (node.hasAttribute('width')) {
26504             node.removeAttribute('width');
26505         }
26506         
26507          
26508         if (node.hasAttribute("style")) {
26509             // pretty basic...
26510             
26511             var styles = node.getAttribute("style").split(";");
26512             var nstyle = [];
26513             Roo.each(styles, function(s) {
26514                 if (!s.match(/:/)) {
26515                     return;
26516                 }
26517                 var kv = s.split(":");
26518                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26519                     return;
26520                 }
26521                 // what ever is left... we allow.
26522                 nstyle.push(s);
26523             });
26524             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26525             if (!nstyle.length) {
26526                 node.removeAttribute('style');
26527             }
26528         }
26529         
26530         return true; // continue doing children..
26531     }
26532 });/**
26533  * @class Roo.htmleditor.FilterWord
26534  * try and clean up all the mess that Word generates.
26535  * 
26536  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26537  
26538  * @constructor
26539  * Run a new Span Filter
26540  * @param {Object} config Configuration options
26541  */
26542
26543 Roo.htmleditor.FilterWord = function(cfg)
26544 {
26545     // no need to apply config.
26546     this.replaceDocBullets(cfg.node);
26547     
26548     this.replaceAname(cfg.node);
26549     // this is disabled as the removal is done by other filters;
26550    // this.walk(cfg.node);
26551     
26552     
26553 }
26554
26555 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26556 {
26557     tag: true,
26558      
26559     
26560     /**
26561      * Clean up MS wordisms...
26562      */
26563     replaceTag : function(node)
26564     {
26565          
26566         // no idea what this does - span with text, replaceds with just text.
26567         if(
26568                 node.nodeName == 'SPAN' &&
26569                 !node.hasAttributes() &&
26570                 node.childNodes.length == 1 &&
26571                 node.firstChild.nodeName == "#text"  
26572         ) {
26573             var textNode = node.firstChild;
26574             node.removeChild(textNode);
26575             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26576                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26577             }
26578             node.parentNode.insertBefore(textNode, node);
26579             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26580                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26581             }
26582             
26583             node.parentNode.removeChild(node);
26584             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26585         }
26586         
26587    
26588         
26589         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26590             node.parentNode.removeChild(node);
26591             return false; // dont do chidlren
26592         }
26593         //Roo.log(node.tagName);
26594         // remove - but keep children..
26595         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26596             //Roo.log('-- removed');
26597             while (node.childNodes.length) {
26598                 var cn = node.childNodes[0];
26599                 node.removeChild(cn);
26600                 node.parentNode.insertBefore(cn, node);
26601                 // move node to parent - and clean it..
26602                 if (cn.nodeType == 1) {
26603                     this.replaceTag(cn);
26604                 }
26605                 
26606             }
26607             node.parentNode.removeChild(node);
26608             /// no need to iterate chidlren = it's got none..
26609             //this.iterateChildren(node, this.cleanWord);
26610             return false; // no need to iterate children.
26611         }
26612         // clean styles
26613         if (node.className.length) {
26614             
26615             var cn = node.className.split(/\W+/);
26616             var cna = [];
26617             Roo.each(cn, function(cls) {
26618                 if (cls.match(/Mso[a-zA-Z]+/)) {
26619                     return;
26620                 }
26621                 cna.push(cls);
26622             });
26623             node.className = cna.length ? cna.join(' ') : '';
26624             if (!cna.length) {
26625                 node.removeAttribute("class");
26626             }
26627         }
26628         
26629         if (node.hasAttribute("lang")) {
26630             node.removeAttribute("lang");
26631         }
26632         
26633         if (node.hasAttribute("style")) {
26634             
26635             var styles = node.getAttribute("style").split(";");
26636             var nstyle = [];
26637             Roo.each(styles, function(s) {
26638                 if (!s.match(/:/)) {
26639                     return;
26640                 }
26641                 var kv = s.split(":");
26642                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26643                     return;
26644                 }
26645                 // what ever is left... we allow.
26646                 nstyle.push(s);
26647             });
26648             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26649             if (!nstyle.length) {
26650                 node.removeAttribute('style');
26651             }
26652         }
26653         return true; // do children
26654         
26655         
26656         
26657     },
26658     
26659     styleToObject: function(node)
26660     {
26661         var styles = (node.getAttribute("style") || '').split(";");
26662         var ret = {};
26663         Roo.each(styles, function(s) {
26664             if (!s.match(/:/)) {
26665                 return;
26666             }
26667             var kv = s.split(":");
26668              
26669             // what ever is left... we allow.
26670             ret[kv[0].trim()] = kv[1];
26671         });
26672         return ret;
26673     },
26674     
26675     
26676     replaceAname : function (doc)
26677     {
26678         // replace all the a/name without..
26679         var aa = Array.from(doc.getElementsByTagName('a'));
26680         for (var i = 0; i  < aa.length; i++) {
26681             var a = aa[i];
26682             if (a.hasAttribute("name")) {
26683                 a.removeAttribute("name");
26684             }
26685             if (a.hasAttribute("href")) {
26686                 continue;
26687             }
26688             // reparent children.
26689             this.removeNodeKeepChildren(a);
26690             
26691         }
26692         
26693         
26694         
26695     },
26696
26697     
26698     
26699     replaceDocBullets : function(doc)
26700     {
26701         // this is a bit odd - but it appears some indents use ql-indent-1
26702          //Roo.log(doc.innerHTML);
26703         
26704         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26705         for( var i = 0; i < listpara.length; i ++) {
26706             listpara[i].className = "MsoListParagraph";
26707         }
26708         
26709         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26710         for( var i = 0; i < listpara.length; i ++) {
26711             listpara[i].className = "MsoListParagraph";
26712         }
26713         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26714         for( var i = 0; i < listpara.length; i ++) {
26715             listpara[i].className = "MsoListParagraph";
26716         }
26717         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26718         for( var i = 0; i < listpara.length; i ++) {
26719             listpara[i].className = "MsoListParagraph";
26720         }
26721         
26722         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26723         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26724         for( var i = 0; i < htwo.length; i ++) {
26725             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26726                 htwo[i].className = "MsoListParagraph";
26727             }
26728         }
26729         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26730         for( var i = 0; i < listpara.length; i ++) {
26731             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26732                 listpara[i].className = "MsoListParagraph";
26733             } else {
26734                 listpara[i].className = "MsoNormalx";
26735             }
26736         }
26737        
26738         listpara = doc.getElementsByClassName('MsoListParagraph');
26739         // Roo.log(doc.innerHTML);
26740         
26741         
26742         
26743         while(listpara.length) {
26744             
26745             this.replaceDocBullet(listpara.item(0));
26746         }
26747       
26748     },
26749     
26750      
26751     
26752     replaceDocBullet : function(p)
26753     {
26754         // gather all the siblings.
26755         var ns = p,
26756             parent = p.parentNode,
26757             doc = parent.ownerDocument,
26758             items = [];
26759             
26760         var listtype = 'ul';   
26761         while (ns) {
26762             if (ns.nodeType != 1) {
26763                 ns = ns.nextSibling;
26764                 continue;
26765             }
26766             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26767                 break;
26768             }
26769             var spans = ns.getElementsByTagName('span');
26770             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26771                 items.push(ns);
26772                 ns = ns.nextSibling;
26773                 has_list = true;
26774                 if (spans.length && spans[0].hasAttribute('style')) {
26775                     var  style = this.styleToObject(spans[0]);
26776                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26777                         listtype = 'ol';
26778                     }
26779                 }
26780                 
26781                 continue;
26782             }
26783             var spans = ns.getElementsByTagName('span');
26784             if (!spans.length) {
26785                 break;
26786             }
26787             var has_list  = false;
26788             for(var i = 0; i < spans.length; i++) {
26789                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26790                     has_list = true;
26791                     break;
26792                 }
26793             }
26794             if (!has_list) {
26795                 break;
26796             }
26797             items.push(ns);
26798             ns = ns.nextSibling;
26799             
26800             
26801         }
26802         if (!items.length) {
26803             ns.className = "";
26804             return;
26805         }
26806         
26807         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26808         parent.insertBefore(ul, p);
26809         var lvl = 0;
26810         var stack = [ ul ];
26811         var last_li = false;
26812         
26813         var margin_to_depth = {};
26814         max_margins = -1;
26815         
26816         items.forEach(function(n, ipos) {
26817             //Roo.log("got innertHMLT=" + n.innerHTML);
26818             
26819             var spans = n.getElementsByTagName('span');
26820             if (!spans.length) {
26821                 //Roo.log("No spans found");
26822                  
26823                 parent.removeChild(n);
26824                 
26825                 
26826                 return; // skip it...
26827             }
26828            
26829                 
26830             var num = 1;
26831             var style = {};
26832             for(var i = 0; i < spans.length; i++) {
26833             
26834                 style = this.styleToObject(spans[i]);
26835                 if (typeof(style['mso-list']) == 'undefined') {
26836                     continue;
26837                 }
26838                 if (listtype == 'ol') {
26839                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26840                 }
26841                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26842                 break;
26843             }
26844             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26845             style = this.styleToObject(n); // mo-list is from the parent node.
26846             if (typeof(style['mso-list']) == 'undefined') {
26847                 //Roo.log("parent is missing level");
26848                   
26849                 parent.removeChild(n);
26850                  
26851                 return;
26852             }
26853             
26854             var margin = style['margin-left'];
26855             if (typeof(margin_to_depth[margin]) == 'undefined') {
26856                 max_margins++;
26857                 margin_to_depth[margin] = max_margins;
26858             }
26859             nlvl = margin_to_depth[margin] ;
26860              
26861             if (nlvl > lvl) {
26862                 //new indent
26863                 var nul = doc.createElement(listtype); // what about number lists...
26864                 if (!last_li) {
26865                     last_li = doc.createElement('li');
26866                     stack[lvl].appendChild(last_li);
26867                 }
26868                 last_li.appendChild(nul);
26869                 stack[nlvl] = nul;
26870                 
26871             }
26872             lvl = nlvl;
26873             
26874             // not starting at 1..
26875             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26876                 stack[nlvl].setAttribute("start", num);
26877             }
26878             
26879             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26880             last_li = nli;
26881             nli.innerHTML = n.innerHTML;
26882             //Roo.log("innerHTML = " + n.innerHTML);
26883             parent.removeChild(n);
26884             
26885              
26886              
26887             
26888         },this);
26889         
26890         
26891         
26892         
26893     }
26894     
26895     
26896     
26897 });
26898 /**
26899  * @class Roo.htmleditor.FilterStyleToTag
26900  * part of the word stuff... - certain 'styles' should be converted to tags.
26901  * eg.
26902  *   font-weight: bold -> bold
26903  *   ?? super / subscrit etc..
26904  * 
26905  * @constructor
26906 * Run a new style to tag filter.
26907 * @param {Object} config Configuration options
26908  */
26909 Roo.htmleditor.FilterStyleToTag = function(cfg)
26910 {
26911     
26912     this.tags = {
26913         B  : [ 'fontWeight' , 'bold'],
26914         I :  [ 'fontStyle' , 'italic'],
26915         //pre :  [ 'font-style' , 'italic'],
26916         // h1.. h6 ?? font-size?
26917         SUP : [ 'verticalAlign' , 'super' ],
26918         SUB : [ 'verticalAlign' , 'sub' ]
26919         
26920         
26921     };
26922     
26923     Roo.apply(this, cfg);
26924      
26925     
26926     this.walk(cfg.node);
26927     
26928     
26929     
26930 }
26931
26932
26933 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26934 {
26935     tag: true, // all tags
26936     
26937     tags : false,
26938     
26939     
26940     replaceTag : function(node)
26941     {
26942         
26943         
26944         if (node.getAttribute("style") === null) {
26945             return true;
26946         }
26947         var inject = [];
26948         for (var k in this.tags) {
26949             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26950                 inject.push(k);
26951                 node.style.removeProperty(this.tags[k][0]);
26952             }
26953         }
26954         if (!inject.length) {
26955             return true; 
26956         }
26957         var cn = Array.from(node.childNodes);
26958         var nn = node;
26959         Roo.each(inject, function(t) {
26960             var nc = node.ownerDocument.createElement(t);
26961             nn.appendChild(nc);
26962             nn = nc;
26963         });
26964         for(var i = 0;i < cn.length;cn++) {
26965             node.removeChild(cn[i]);
26966             nn.appendChild(cn[i]);
26967         }
26968         return true /// iterate thru
26969     }
26970     
26971 })/**
26972  * @class Roo.htmleditor.FilterLongBr
26973  * BR/BR/BR - keep a maximum of 2...
26974  * @constructor
26975  * Run a new Long BR Filter
26976  * @param {Object} config Configuration options
26977  */
26978
26979 Roo.htmleditor.FilterLongBr = function(cfg)
26980 {
26981     // no need to apply config.
26982     this.walk(cfg.node);
26983 }
26984
26985 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26986 {
26987     
26988      
26989     tag : 'BR',
26990     
26991      
26992     replaceTag : function(node)
26993     {
26994         
26995         var ps = node.nextSibling;
26996         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26997             ps = ps.nextSibling;
26998         }
26999         
27000         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27001             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27002             return false;
27003         }
27004         
27005         if (!ps || ps.nodeType != 1) {
27006             return false;
27007         }
27008         
27009         if (!ps || ps.tagName != 'BR') {
27010            
27011             return false;
27012         }
27013         
27014         
27015         
27016         
27017         
27018         if (!node.previousSibling) {
27019             return false;
27020         }
27021         var ps = node.previousSibling;
27022         
27023         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27024             ps = ps.previousSibling;
27025         }
27026         if (!ps || ps.nodeType != 1) {
27027             return false;
27028         }
27029         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27030         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27031             return false;
27032         }
27033         
27034         node.parentNode.removeChild(node); // remove me...
27035         
27036         return false; // no need to do children
27037
27038     }
27039     
27040 }); 
27041
27042 /**
27043  * @class Roo.htmleditor.FilterBlock
27044  * removes id / data-block and contenteditable that are associated with blocks
27045  * usage should be done on a cloned copy of the dom
27046  * @constructor
27047 * Run a new Attribute Filter { node : xxxx }}
27048 * @param {Object} config Configuration options
27049  */
27050 Roo.htmleditor.FilterBlock = function(cfg)
27051 {
27052     Roo.apply(this, cfg);
27053     var qa = cfg.node.querySelectorAll;
27054     this.removeAttributes('data-block');
27055     this.removeAttributes('contenteditable');
27056     this.removeAttributes('id');
27057     
27058 }
27059
27060 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27061 {
27062     node: true, // all tags
27063      
27064      
27065     removeAttributes : function(attr)
27066     {
27067         var ar = this.node.querySelectorAll('*[' + attr + ']');
27068         for (var i =0;i<ar.length;i++) {
27069             ar[i].removeAttribute(attr);
27070         }
27071     }
27072         
27073         
27074         
27075     
27076 });
27077 /***
27078  * This is based loosely on tinymce 
27079  * @class Roo.htmleditor.TidySerializer
27080  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27081  * @constructor
27082  * @method Serializer
27083  * @param {Object} settings Name/value settings object.
27084  */
27085
27086
27087 Roo.htmleditor.TidySerializer = function(settings)
27088 {
27089     Roo.apply(this, settings);
27090     
27091     this.writer = new Roo.htmleditor.TidyWriter(settings);
27092     
27093     
27094
27095 };
27096 Roo.htmleditor.TidySerializer.prototype = {
27097     
27098     /**
27099      * @param {boolean} inner do the inner of the node.
27100      */
27101     inner : false,
27102     
27103     writer : false,
27104     
27105     /**
27106     * Serializes the specified node into a string.
27107     *
27108     * @example
27109     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27110     * @method serialize
27111     * @param {DomElement} node Node instance to serialize.
27112     * @return {String} String with HTML based on DOM tree.
27113     */
27114     serialize : function(node) {
27115         
27116         // = settings.validate;
27117         var writer = this.writer;
27118         var self  = this;
27119         this.handlers = {
27120             // #text
27121             3: function(node) {
27122                 
27123                 writer.text(node.nodeValue, node);
27124             },
27125             // #comment
27126             8: function(node) {
27127                 writer.comment(node.nodeValue);
27128             },
27129             // Processing instruction
27130             7: function(node) {
27131                 writer.pi(node.name, node.nodeValue);
27132             },
27133             // Doctype
27134             10: function(node) {
27135                 writer.doctype(node.nodeValue);
27136             },
27137             // CDATA
27138             4: function(node) {
27139                 writer.cdata(node.nodeValue);
27140             },
27141             // Document fragment
27142             11: function(node) {
27143                 node = node.firstChild;
27144                 if (!node) {
27145                     return;
27146                 }
27147                 while(node) {
27148                     self.walk(node);
27149                     node = node.nextSibling
27150                 }
27151             }
27152         };
27153         writer.reset();
27154         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27155         return writer.getContent();
27156     },
27157
27158     walk: function(node)
27159     {
27160         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27161             handler = this.handlers[node.nodeType];
27162             
27163         if (handler) {
27164             handler(node);
27165             return;
27166         }
27167     
27168         var name = node.nodeName;
27169         var isEmpty = node.childNodes.length < 1;
27170       
27171         var writer = this.writer;
27172         var attrs = node.attributes;
27173         // Sort attributes
27174         
27175         writer.start(node.nodeName, attrs, isEmpty, node);
27176         if (isEmpty) {
27177             return;
27178         }
27179         node = node.firstChild;
27180         if (!node) {
27181             writer.end(name);
27182             return;
27183         }
27184         while (node) {
27185             this.walk(node);
27186             node = node.nextSibling;
27187         }
27188         writer.end(name);
27189         
27190     
27191     }
27192     // Serialize element and treat all non elements as fragments
27193    
27194 }; 
27195
27196 /***
27197  * This is based loosely on tinymce 
27198  * @class Roo.htmleditor.TidyWriter
27199  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27200  *
27201  * Known issues?
27202  * - not tested much with 'PRE' formated elements.
27203  * 
27204  *
27205  *
27206  */
27207
27208 Roo.htmleditor.TidyWriter = function(settings)
27209 {
27210     
27211     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27212     Roo.apply(this, settings);
27213     this.html = [];
27214     this.state = [];
27215      
27216     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27217   
27218 }
27219 Roo.htmleditor.TidyWriter.prototype = {
27220
27221  
27222     state : false,
27223     
27224     indent :  '  ',
27225     
27226     // part of state...
27227     indentstr : '',
27228     in_pre: false,
27229     in_inline : false,
27230     last_inline : false,
27231     encode : false,
27232      
27233     
27234             /**
27235     * Writes the a start element such as <p id="a">.
27236     *
27237     * @method start
27238     * @param {String} name Name of the element.
27239     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27240     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27241     */
27242     start: function(name, attrs, empty, node)
27243     {
27244         var i, l, attr, value;
27245         
27246         // there are some situations where adding line break && indentation will not work. will not work.
27247         // <span / b / i ... formating?
27248         
27249         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27250         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27251         
27252         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27253         
27254         var add_lb = name == 'BR' ? false : in_inline;
27255         
27256         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27257             i_inline = false;
27258         }
27259
27260         var indentstr =  this.indentstr;
27261         
27262         // e_inline = elements that can be inline, but still allow \n before and after?
27263         // only 'BR' ??? any others?
27264         
27265         // ADD LINE BEFORE tage
27266         if (!this.in_pre) {
27267             if (in_inline) {
27268                 //code
27269                 if (name == 'BR') {
27270                     this.addLine();
27271                 } else if (this.lastElementEndsWS()) {
27272                     this.addLine();
27273                 } else{
27274                     // otherwise - no new line. (and dont indent.)
27275                     indentstr = '';
27276                 }
27277                 
27278             } else {
27279                 this.addLine();
27280             }
27281         } else {
27282             indentstr = '';
27283         }
27284         
27285         this.html.push(indentstr + '<', name.toLowerCase());
27286         
27287         if (attrs) {
27288             for (i = 0, l = attrs.length; i < l; i++) {
27289                 attr = attrs[i];
27290                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27291             }
27292         }
27293      
27294         if (empty) {
27295             if (is_short) {
27296                 this.html[this.html.length] = '/>';
27297             } else {
27298                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27299             }
27300             var e_inline = name == 'BR' ? false : this.in_inline;
27301             
27302             if (!e_inline && !this.in_pre) {
27303                 this.addLine();
27304             }
27305             return;
27306         
27307         }
27308         // not empty..
27309         this.html[this.html.length] = '>';
27310         
27311         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27312         /*
27313         if (!in_inline && !in_pre) {
27314             var cn = node.firstChild;
27315             while(cn) {
27316                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27317                     in_inline = true
27318                     break;
27319                 }
27320                 cn = cn.nextSibling;
27321             }
27322              
27323         }
27324         */
27325         
27326         
27327         this.pushState({
27328             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27329             in_pre : in_pre,
27330             in_inline :  in_inline
27331         });
27332         // add a line after if we are not in a
27333         
27334         if (!in_inline && !in_pre) {
27335             this.addLine();
27336         }
27337         
27338             
27339          
27340         
27341     },
27342     
27343     lastElementEndsWS : function()
27344     {
27345         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27346         if (value === false) {
27347             return true;
27348         }
27349         return value.match(/\s+$/);
27350         
27351     },
27352     
27353     /**
27354      * Writes the a end element such as </p>.
27355      *
27356      * @method end
27357      * @param {String} name Name of the element.
27358      */
27359     end: function(name) {
27360         var value;
27361         this.popState();
27362         var indentstr = '';
27363         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27364         
27365         if (!this.in_pre && !in_inline) {
27366             this.addLine();
27367             indentstr  = this.indentstr;
27368         }
27369         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27370         this.last_inline = in_inline;
27371         
27372         // pop the indent state..
27373     },
27374     /**
27375      * Writes a text node.
27376      *
27377      * In pre - we should not mess with the contents.
27378      * 
27379      *
27380      * @method text
27381      * @param {String} text String to write out.
27382      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27383      */
27384     text: function(in_text, node)
27385     {
27386         // if not in whitespace critical
27387         if (in_text.length < 1) {
27388             return;
27389         }
27390         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27391         
27392         if (this.in_pre) {
27393             this.html[this.html.length] =  text;
27394             return;   
27395         }
27396         
27397         if (this.in_inline) {
27398             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27399             if (text != ' ') {
27400                 text = text.replace(/\s+/,' ');  // all white space to single white space
27401                 
27402                     
27403                 // if next tag is '<BR>', then we can trim right..
27404                 if (node.nextSibling &&
27405                     node.nextSibling.nodeType == 1 &&
27406                     node.nextSibling.nodeName == 'BR' )
27407                 {
27408                     text = text.replace(/\s+$/g,'');
27409                 }
27410                 // if previous tag was a BR, we can also trim..
27411                 if (node.previousSibling &&
27412                     node.previousSibling.nodeType == 1 &&
27413                     node.previousSibling.nodeName == 'BR' )
27414                 {
27415                     text = this.indentstr +  text.replace(/^\s+/g,'');
27416                 }
27417                 if (text.match(/\n/)) {
27418                     text = text.replace(
27419                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27420                     );
27421                     // remoeve the last whitespace / line break.
27422                     text = text.replace(/\n\s+$/,'');
27423                 }
27424                 // repace long lines
27425                 
27426             }
27427              
27428             this.html[this.html.length] =  text;
27429             return;   
27430         }
27431         // see if previous element was a inline element.
27432         var indentstr = this.indentstr;
27433    
27434         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27435         
27436         // should trim left?
27437         if (node.previousSibling &&
27438             node.previousSibling.nodeType == 1 &&
27439             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27440         {
27441             indentstr = '';
27442             
27443         } else {
27444             this.addLine();
27445             text = text.replace(/^\s+/,''); // trim left
27446           
27447         }
27448         // should trim right?
27449         if (node.nextSibling &&
27450             node.nextSibling.nodeType == 1 &&
27451             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27452         {
27453           // noop
27454             
27455         }  else {
27456             text = text.replace(/\s+$/,''); // trim right
27457         }
27458          
27459               
27460         
27461         
27462         
27463         if (text.length < 1) {
27464             return;
27465         }
27466         if (!text.match(/\n/)) {
27467             this.html.push(indentstr + text);
27468             return;
27469         }
27470         
27471         text = this.indentstr + text.replace(
27472             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27473         );
27474         // remoeve the last whitespace / line break.
27475         text = text.replace(/\s+$/,''); 
27476         
27477         this.html.push(text);
27478         
27479         // split and indent..
27480         
27481         
27482     },
27483     /**
27484      * Writes a cdata node such as <![CDATA[data]]>.
27485      *
27486      * @method cdata
27487      * @param {String} text String to write out inside the cdata.
27488      */
27489     cdata: function(text) {
27490         this.html.push('<![CDATA[', text, ']]>');
27491     },
27492     /**
27493     * Writes a comment node such as <!-- Comment -->.
27494     *
27495     * @method cdata
27496     * @param {String} text String to write out inside the comment.
27497     */
27498    comment: function(text) {
27499        this.html.push('<!--', text, '-->');
27500    },
27501     /**
27502      * Writes a PI node such as <?xml attr="value" ?>.
27503      *
27504      * @method pi
27505      * @param {String} name Name of the pi.
27506      * @param {String} text String to write out inside the pi.
27507      */
27508     pi: function(name, text) {
27509         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27510         this.indent != '' && this.html.push('\n');
27511     },
27512     /**
27513      * Writes a doctype node such as <!DOCTYPE data>.
27514      *
27515      * @method doctype
27516      * @param {String} text String to write out inside the doctype.
27517      */
27518     doctype: function(text) {
27519         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27520     },
27521     /**
27522      * Resets the internal buffer if one wants to reuse the writer.
27523      *
27524      * @method reset
27525      */
27526     reset: function() {
27527         this.html.length = 0;
27528         this.state = [];
27529         this.pushState({
27530             indentstr : '',
27531             in_pre : false, 
27532             in_inline : false
27533         })
27534     },
27535     /**
27536      * Returns the contents that got serialized.
27537      *
27538      * @method getContent
27539      * @return {String} HTML contents that got written down.
27540      */
27541     getContent: function() {
27542         return this.html.join('').replace(/\n$/, '');
27543     },
27544     
27545     pushState : function(cfg)
27546     {
27547         this.state.push(cfg);
27548         Roo.apply(this, cfg);
27549     },
27550     
27551     popState : function()
27552     {
27553         if (this.state.length < 1) {
27554             return; // nothing to push
27555         }
27556         var cfg = {
27557             in_pre: false,
27558             indentstr : ''
27559         };
27560         this.state.pop();
27561         if (this.state.length > 0) {
27562             cfg = this.state[this.state.length-1]; 
27563         }
27564         Roo.apply(this, cfg);
27565     },
27566     
27567     addLine: function()
27568     {
27569         if (this.html.length < 1) {
27570             return;
27571         }
27572         
27573         
27574         var value = this.html[this.html.length - 1];
27575         if (value.length > 0 && '\n' !== value) {
27576             this.html.push('\n');
27577         }
27578     }
27579     
27580     
27581 //'pre script noscript style textarea video audio iframe object code'
27582 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27583 // inline 
27584 };
27585
27586 Roo.htmleditor.TidyWriter.inline_elements = [
27587         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27588         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27589 ];
27590 Roo.htmleditor.TidyWriter.shortend_elements = [
27591     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27592     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27593 ];
27594
27595 Roo.htmleditor.TidyWriter.whitespace_elements = [
27596     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27597 ];/***
27598  * This is based loosely on tinymce 
27599  * @class Roo.htmleditor.TidyEntities
27600  * @static
27601  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27602  *
27603  * Not 100% sure this is actually used or needed.
27604  */
27605
27606 Roo.htmleditor.TidyEntities = {
27607     
27608     /**
27609      * initialize data..
27610      */
27611     init : function (){
27612      
27613         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27614        
27615     },
27616
27617
27618     buildEntitiesLookup: function(items, radix) {
27619         var i, chr, entity, lookup = {};
27620         if (!items) {
27621             return {};
27622         }
27623         items = typeof(items) == 'string' ? items.split(',') : items;
27624         radix = radix || 10;
27625         // Build entities lookup table
27626         for (i = 0; i < items.length; i += 2) {
27627             chr = String.fromCharCode(parseInt(items[i], radix));
27628             // Only add non base entities
27629             if (!this.baseEntities[chr]) {
27630                 entity = '&' + items[i + 1] + ';';
27631                 lookup[chr] = entity;
27632                 lookup[entity] = chr;
27633             }
27634         }
27635         return lookup;
27636         
27637     },
27638     
27639     asciiMap : {
27640             128: '€',
27641             130: '‚',
27642             131: 'ƒ',
27643             132: '„',
27644             133: '…',
27645             134: '†',
27646             135: '‡',
27647             136: 'ˆ',
27648             137: '‰',
27649             138: 'Š',
27650             139: '‹',
27651             140: 'Œ',
27652             142: 'Ž',
27653             145: '‘',
27654             146: '’',
27655             147: '“',
27656             148: '”',
27657             149: '•',
27658             150: '–',
27659             151: '—',
27660             152: '˜',
27661             153: '™',
27662             154: 'š',
27663             155: '›',
27664             156: 'œ',
27665             158: 'ž',
27666             159: 'Ÿ'
27667     },
27668     // Raw entities
27669     baseEntities : {
27670         '"': '&quot;',
27671         // Needs to be escaped since the YUI compressor would otherwise break the code
27672         '\'': '&#39;',
27673         '<': '&lt;',
27674         '>': '&gt;',
27675         '&': '&amp;',
27676         '`': '&#96;'
27677     },
27678     // Reverse lookup table for raw entities
27679     reverseEntities : {
27680         '&lt;': '<',
27681         '&gt;': '>',
27682         '&amp;': '&',
27683         '&quot;': '"',
27684         '&apos;': '\''
27685     },
27686     
27687     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27688     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27689     rawCharsRegExp : /[<>&\"\']/g,
27690     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27691     namedEntities  : false,
27692     namedEntitiesData : [ 
27693         '50',
27694         'nbsp',
27695         '51',
27696         'iexcl',
27697         '52',
27698         'cent',
27699         '53',
27700         'pound',
27701         '54',
27702         'curren',
27703         '55',
27704         'yen',
27705         '56',
27706         'brvbar',
27707         '57',
27708         'sect',
27709         '58',
27710         'uml',
27711         '59',
27712         'copy',
27713         '5a',
27714         'ordf',
27715         '5b',
27716         'laquo',
27717         '5c',
27718         'not',
27719         '5d',
27720         'shy',
27721         '5e',
27722         'reg',
27723         '5f',
27724         'macr',
27725         '5g',
27726         'deg',
27727         '5h',
27728         'plusmn',
27729         '5i',
27730         'sup2',
27731         '5j',
27732         'sup3',
27733         '5k',
27734         'acute',
27735         '5l',
27736         'micro',
27737         '5m',
27738         'para',
27739         '5n',
27740         'middot',
27741         '5o',
27742         'cedil',
27743         '5p',
27744         'sup1',
27745         '5q',
27746         'ordm',
27747         '5r',
27748         'raquo',
27749         '5s',
27750         'frac14',
27751         '5t',
27752         'frac12',
27753         '5u',
27754         'frac34',
27755         '5v',
27756         'iquest',
27757         '60',
27758         'Agrave',
27759         '61',
27760         'Aacute',
27761         '62',
27762         'Acirc',
27763         '63',
27764         'Atilde',
27765         '64',
27766         'Auml',
27767         '65',
27768         'Aring',
27769         '66',
27770         'AElig',
27771         '67',
27772         'Ccedil',
27773         '68',
27774         'Egrave',
27775         '69',
27776         'Eacute',
27777         '6a',
27778         'Ecirc',
27779         '6b',
27780         'Euml',
27781         '6c',
27782         'Igrave',
27783         '6d',
27784         'Iacute',
27785         '6e',
27786         'Icirc',
27787         '6f',
27788         'Iuml',
27789         '6g',
27790         'ETH',
27791         '6h',
27792         'Ntilde',
27793         '6i',
27794         'Ograve',
27795         '6j',
27796         'Oacute',
27797         '6k',
27798         'Ocirc',
27799         '6l',
27800         'Otilde',
27801         '6m',
27802         'Ouml',
27803         '6n',
27804         'times',
27805         '6o',
27806         'Oslash',
27807         '6p',
27808         'Ugrave',
27809         '6q',
27810         'Uacute',
27811         '6r',
27812         'Ucirc',
27813         '6s',
27814         'Uuml',
27815         '6t',
27816         'Yacute',
27817         '6u',
27818         'THORN',
27819         '6v',
27820         'szlig',
27821         '70',
27822         'agrave',
27823         '71',
27824         'aacute',
27825         '72',
27826         'acirc',
27827         '73',
27828         'atilde',
27829         '74',
27830         'auml',
27831         '75',
27832         'aring',
27833         '76',
27834         'aelig',
27835         '77',
27836         'ccedil',
27837         '78',
27838         'egrave',
27839         '79',
27840         'eacute',
27841         '7a',
27842         'ecirc',
27843         '7b',
27844         'euml',
27845         '7c',
27846         'igrave',
27847         '7d',
27848         'iacute',
27849         '7e',
27850         'icirc',
27851         '7f',
27852         'iuml',
27853         '7g',
27854         'eth',
27855         '7h',
27856         'ntilde',
27857         '7i',
27858         'ograve',
27859         '7j',
27860         'oacute',
27861         '7k',
27862         'ocirc',
27863         '7l',
27864         'otilde',
27865         '7m',
27866         'ouml',
27867         '7n',
27868         'divide',
27869         '7o',
27870         'oslash',
27871         '7p',
27872         'ugrave',
27873         '7q',
27874         'uacute',
27875         '7r',
27876         'ucirc',
27877         '7s',
27878         'uuml',
27879         '7t',
27880         'yacute',
27881         '7u',
27882         'thorn',
27883         '7v',
27884         'yuml',
27885         'ci',
27886         'fnof',
27887         'sh',
27888         'Alpha',
27889         'si',
27890         'Beta',
27891         'sj',
27892         'Gamma',
27893         'sk',
27894         'Delta',
27895         'sl',
27896         'Epsilon',
27897         'sm',
27898         'Zeta',
27899         'sn',
27900         'Eta',
27901         'so',
27902         'Theta',
27903         'sp',
27904         'Iota',
27905         'sq',
27906         'Kappa',
27907         'sr',
27908         'Lambda',
27909         'ss',
27910         'Mu',
27911         'st',
27912         'Nu',
27913         'su',
27914         'Xi',
27915         'sv',
27916         'Omicron',
27917         't0',
27918         'Pi',
27919         't1',
27920         'Rho',
27921         't3',
27922         'Sigma',
27923         't4',
27924         'Tau',
27925         't5',
27926         'Upsilon',
27927         't6',
27928         'Phi',
27929         't7',
27930         'Chi',
27931         't8',
27932         'Psi',
27933         't9',
27934         'Omega',
27935         'th',
27936         'alpha',
27937         'ti',
27938         'beta',
27939         'tj',
27940         'gamma',
27941         'tk',
27942         'delta',
27943         'tl',
27944         'epsilon',
27945         'tm',
27946         'zeta',
27947         'tn',
27948         'eta',
27949         'to',
27950         'theta',
27951         'tp',
27952         'iota',
27953         'tq',
27954         'kappa',
27955         'tr',
27956         'lambda',
27957         'ts',
27958         'mu',
27959         'tt',
27960         'nu',
27961         'tu',
27962         'xi',
27963         'tv',
27964         'omicron',
27965         'u0',
27966         'pi',
27967         'u1',
27968         'rho',
27969         'u2',
27970         'sigmaf',
27971         'u3',
27972         'sigma',
27973         'u4',
27974         'tau',
27975         'u5',
27976         'upsilon',
27977         'u6',
27978         'phi',
27979         'u7',
27980         'chi',
27981         'u8',
27982         'psi',
27983         'u9',
27984         'omega',
27985         'uh',
27986         'thetasym',
27987         'ui',
27988         'upsih',
27989         'um',
27990         'piv',
27991         '812',
27992         'bull',
27993         '816',
27994         'hellip',
27995         '81i',
27996         'prime',
27997         '81j',
27998         'Prime',
27999         '81u',
28000         'oline',
28001         '824',
28002         'frasl',
28003         '88o',
28004         'weierp',
28005         '88h',
28006         'image',
28007         '88s',
28008         'real',
28009         '892',
28010         'trade',
28011         '89l',
28012         'alefsym',
28013         '8cg',
28014         'larr',
28015         '8ch',
28016         'uarr',
28017         '8ci',
28018         'rarr',
28019         '8cj',
28020         'darr',
28021         '8ck',
28022         'harr',
28023         '8dl',
28024         'crarr',
28025         '8eg',
28026         'lArr',
28027         '8eh',
28028         'uArr',
28029         '8ei',
28030         'rArr',
28031         '8ej',
28032         'dArr',
28033         '8ek',
28034         'hArr',
28035         '8g0',
28036         'forall',
28037         '8g2',
28038         'part',
28039         '8g3',
28040         'exist',
28041         '8g5',
28042         'empty',
28043         '8g7',
28044         'nabla',
28045         '8g8',
28046         'isin',
28047         '8g9',
28048         'notin',
28049         '8gb',
28050         'ni',
28051         '8gf',
28052         'prod',
28053         '8gh',
28054         'sum',
28055         '8gi',
28056         'minus',
28057         '8gn',
28058         'lowast',
28059         '8gq',
28060         'radic',
28061         '8gt',
28062         'prop',
28063         '8gu',
28064         'infin',
28065         '8h0',
28066         'ang',
28067         '8h7',
28068         'and',
28069         '8h8',
28070         'or',
28071         '8h9',
28072         'cap',
28073         '8ha',
28074         'cup',
28075         '8hb',
28076         'int',
28077         '8hk',
28078         'there4',
28079         '8hs',
28080         'sim',
28081         '8i5',
28082         'cong',
28083         '8i8',
28084         'asymp',
28085         '8j0',
28086         'ne',
28087         '8j1',
28088         'equiv',
28089         '8j4',
28090         'le',
28091         '8j5',
28092         'ge',
28093         '8k2',
28094         'sub',
28095         '8k3',
28096         'sup',
28097         '8k4',
28098         'nsub',
28099         '8k6',
28100         'sube',
28101         '8k7',
28102         'supe',
28103         '8kl',
28104         'oplus',
28105         '8kn',
28106         'otimes',
28107         '8l5',
28108         'perp',
28109         '8m5',
28110         'sdot',
28111         '8o8',
28112         'lceil',
28113         '8o9',
28114         'rceil',
28115         '8oa',
28116         'lfloor',
28117         '8ob',
28118         'rfloor',
28119         '8p9',
28120         'lang',
28121         '8pa',
28122         'rang',
28123         '9ea',
28124         'loz',
28125         '9j0',
28126         'spades',
28127         '9j3',
28128         'clubs',
28129         '9j5',
28130         'hearts',
28131         '9j6',
28132         'diams',
28133         'ai',
28134         'OElig',
28135         'aj',
28136         'oelig',
28137         'b0',
28138         'Scaron',
28139         'b1',
28140         'scaron',
28141         'bo',
28142         'Yuml',
28143         'm6',
28144         'circ',
28145         'ms',
28146         'tilde',
28147         '802',
28148         'ensp',
28149         '803',
28150         'emsp',
28151         '809',
28152         'thinsp',
28153         '80c',
28154         'zwnj',
28155         '80d',
28156         'zwj',
28157         '80e',
28158         'lrm',
28159         '80f',
28160         'rlm',
28161         '80j',
28162         'ndash',
28163         '80k',
28164         'mdash',
28165         '80o',
28166         'lsquo',
28167         '80p',
28168         'rsquo',
28169         '80q',
28170         'sbquo',
28171         '80s',
28172         'ldquo',
28173         '80t',
28174         'rdquo',
28175         '80u',
28176         'bdquo',
28177         '810',
28178         'dagger',
28179         '811',
28180         'Dagger',
28181         '81g',
28182         'permil',
28183         '81p',
28184         'lsaquo',
28185         '81q',
28186         'rsaquo',
28187         '85c',
28188         'euro'
28189     ],
28190
28191          
28192     /**
28193      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28194      *
28195      * @method encodeRaw
28196      * @param {String} text Text to encode.
28197      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28198      * @return {String} Entity encoded text.
28199      */
28200     encodeRaw: function(text, attr)
28201     {
28202         var t = this;
28203         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28204             return t.baseEntities[chr] || chr;
28205         });
28206     },
28207     /**
28208      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28209      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28210      * and is exposed as the DOMUtils.encode function.
28211      *
28212      * @method encodeAllRaw
28213      * @param {String} text Text to encode.
28214      * @return {String} Entity encoded text.
28215      */
28216     encodeAllRaw: function(text) {
28217         var t = this;
28218         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28219             return t.baseEntities[chr] || chr;
28220         });
28221     },
28222     /**
28223      * Encodes the specified string using numeric entities. The core entities will be
28224      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28225      *
28226      * @method encodeNumeric
28227      * @param {String} text Text to encode.
28228      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28229      * @return {String} Entity encoded text.
28230      */
28231     encodeNumeric: function(text, attr) {
28232         var t = this;
28233         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28234             // Multi byte sequence convert it to a single entity
28235             if (chr.length > 1) {
28236                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28237             }
28238             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28239         });
28240     },
28241     /**
28242      * Encodes the specified string using named entities. The core entities will be encoded
28243      * as named ones but all non lower ascii characters will be encoded into named entities.
28244      *
28245      * @method encodeNamed
28246      * @param {String} text Text to encode.
28247      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28248      * @param {Object} entities Optional parameter with entities to use.
28249      * @return {String} Entity encoded text.
28250      */
28251     encodeNamed: function(text, attr, entities) {
28252         var t = this;
28253         entities = entities || this.namedEntities;
28254         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28255             return t.baseEntities[chr] || entities[chr] || chr;
28256         });
28257     },
28258     /**
28259      * Returns an encode function based on the name(s) and it's optional entities.
28260      *
28261      * @method getEncodeFunc
28262      * @param {String} name Comma separated list of encoders for example named,numeric.
28263      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28264      * @return {function} Encode function to be used.
28265      */
28266     getEncodeFunc: function(name, entities) {
28267         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28268         var t = this;
28269         function encodeNamedAndNumeric(text, attr) {
28270             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28271                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28272             });
28273         }
28274
28275         function encodeCustomNamed(text, attr) {
28276             return t.encodeNamed(text, attr, entities);
28277         }
28278         // Replace + with , to be compatible with previous TinyMCE versions
28279         name = this.makeMap(name.replace(/\+/g, ','));
28280         // Named and numeric encoder
28281         if (name.named && name.numeric) {
28282             return this.encodeNamedAndNumeric;
28283         }
28284         // Named encoder
28285         if (name.named) {
28286             // Custom names
28287             if (entities) {
28288                 return encodeCustomNamed;
28289             }
28290             return this.encodeNamed;
28291         }
28292         // Numeric
28293         if (name.numeric) {
28294             return this.encodeNumeric;
28295         }
28296         // Raw encoder
28297         return this.encodeRaw;
28298     },
28299     /**
28300      * Decodes the specified string, this will replace entities with raw UTF characters.
28301      *
28302      * @method decode
28303      * @param {String} text Text to entity decode.
28304      * @return {String} Entity decoded string.
28305      */
28306     decode: function(text)
28307     {
28308         var  t = this;
28309         return text.replace(this.entityRegExp, function(all, numeric) {
28310             if (numeric) {
28311                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28312                 // Support upper UTF
28313                 if (numeric > 65535) {
28314                     numeric -= 65536;
28315                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28316                 }
28317                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28318             }
28319             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28320         });
28321     },
28322     nativeDecode : function (text) {
28323         return text;
28324     },
28325     makeMap : function (items, delim, map) {
28326                 var i;
28327                 items = items || [];
28328                 delim = delim || ',';
28329                 if (typeof items == "string") {
28330                         items = items.split(delim);
28331                 }
28332                 map = map || {};
28333                 i = items.length;
28334                 while (i--) {
28335                         map[items[i]] = {};
28336                 }
28337                 return map;
28338         }
28339 };
28340     
28341     
28342     
28343 Roo.htmleditor.TidyEntities.init();
28344 /**
28345  * @class Roo.htmleditor.KeyEnter
28346  * Handle Enter press..
28347  * @cfg {Roo.HtmlEditorCore} core the editor.
28348  * @constructor
28349  * Create a new Filter.
28350  * @param {Object} config Configuration options
28351  */
28352
28353
28354
28355
28356
28357 Roo.htmleditor.KeyEnter = function(cfg) {
28358     Roo.apply(this, cfg);
28359     // this does not actually call walk as it's really just a abstract class
28360  
28361     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28362 }
28363
28364 //Roo.htmleditor.KeyEnter.i = 0;
28365
28366
28367 Roo.htmleditor.KeyEnter.prototype = {
28368     
28369     core : false,
28370     
28371     keypress : function(e)
28372     {
28373         if (e.charCode != 13 && e.charCode != 10) {
28374             Roo.log([e.charCode,e]);
28375             return true;
28376         }
28377         e.preventDefault();
28378         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28379         var doc = this.core.doc;
28380           //add a new line
28381        
28382     
28383         var sel = this.core.getSelection();
28384         var range = sel.getRangeAt(0);
28385         var n = range.commonAncestorContainer;
28386         var pc = range.closest([ 'ol', 'ul']);
28387         var pli = range.closest('li');
28388         if (!pc || e.ctrlKey) {
28389             // on it list, or ctrl pressed.
28390             if (!e.ctrlKey) {
28391                 sel.insertNode('br', 'after'); 
28392             } else {
28393                 // only do this if we have ctrl key..
28394                 var br = doc.createElement('br');
28395                 br.className = 'clear';
28396                 br.setAttribute('style', 'clear: both');
28397                 sel.insertNode(br, 'after'); 
28398             }
28399             
28400          
28401             this.core.undoManager.addEvent();
28402             this.core.fireEditorEvent(e);
28403             return false;
28404         }
28405         
28406         // deal with <li> insetion
28407         if (pli.innerText.trim() == '' &&
28408             pli.previousSibling &&
28409             pli.previousSibling.nodeName == 'LI' &&
28410             pli.previousSibling.innerText.trim() ==  '') {
28411             pli.parentNode.removeChild(pli.previousSibling);
28412             sel.cursorAfter(pc);
28413             this.core.undoManager.addEvent();
28414             this.core.fireEditorEvent(e);
28415             return false;
28416         }
28417     
28418         var li = doc.createElement('LI');
28419         li.innerHTML = '&nbsp;';
28420         if (!pli || !pli.firstSibling) {
28421             pc.appendChild(li);
28422         } else {
28423             pli.parentNode.insertBefore(li, pli.firstSibling);
28424         }
28425         sel.cursorText (li.firstChild);
28426       
28427         this.core.undoManager.addEvent();
28428         this.core.fireEditorEvent(e);
28429
28430         return false;
28431         
28432     
28433         
28434         
28435          
28436     }
28437 };
28438      
28439 /**
28440  * @class Roo.htmleditor.Block
28441  * Base class for html editor blocks - do not use it directly .. extend it..
28442  * @cfg {DomElement} node The node to apply stuff to.
28443  * @cfg {String} friendly_name the name that appears in the context bar about this block
28444  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28445  
28446  * @constructor
28447  * Create a new Filter.
28448  * @param {Object} config Configuration options
28449  */
28450
28451 Roo.htmleditor.Block  = function(cfg)
28452 {
28453     // do nothing .. should not be called really.
28454 }
28455 /**
28456  * factory method to get the block from an element (using cache if necessary)
28457  * @static
28458  * @param {HtmlElement} the dom element
28459  */
28460 Roo.htmleditor.Block.factory = function(node)
28461 {
28462     var cc = Roo.htmleditor.Block.cache;
28463     var id = Roo.get(node).id;
28464     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28465         Roo.htmleditor.Block.cache[id].readElement(node);
28466         return Roo.htmleditor.Block.cache[id];
28467     }
28468     var db  = node.getAttribute('data-block');
28469     if (!db) {
28470         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28471     }
28472     var cls = Roo.htmleditor['Block' + db];
28473     if (typeof(cls) == 'undefined') {
28474         //Roo.log(node.getAttribute('data-block'));
28475         Roo.log("OOps missing block : " + 'Block' + db);
28476         return false;
28477     }
28478     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28479     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28480 };
28481
28482 /**
28483  * initalize all Elements from content that are 'blockable'
28484  * @static
28485  * @param the body element
28486  */
28487 Roo.htmleditor.Block.initAll = function(body, type)
28488 {
28489     if (typeof(type) == 'undefined') {
28490         var ia = Roo.htmleditor.Block.initAll;
28491         ia(body,'table');
28492         ia(body,'td');
28493         ia(body,'figure');
28494         return;
28495     }
28496     Roo.each(Roo.get(body).query(type), function(e) {
28497         Roo.htmleditor.Block.factory(e);    
28498     },this);
28499 };
28500 // question goes here... do we need to clear out this cache sometimes?
28501 // or show we make it relivant to the htmleditor.
28502 Roo.htmleditor.Block.cache = {};
28503
28504 Roo.htmleditor.Block.prototype = {
28505     
28506     node : false,
28507     
28508      // used by context menu
28509     friendly_name : 'Based Block',
28510     
28511     // text for button to delete this element
28512     deleteTitle : false,
28513     
28514     context : false,
28515     /**
28516      * Update a node with values from this object
28517      * @param {DomElement} node
28518      */
28519     updateElement : function(node)
28520     {
28521         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28522     },
28523      /**
28524      * convert to plain HTML for calling insertAtCursor..
28525      */
28526     toHTML : function()
28527     {
28528         return Roo.DomHelper.markup(this.toObject());
28529     },
28530     /**
28531      * used by readEleemnt to extract data from a node
28532      * may need improving as it's pretty basic
28533      
28534      * @param {DomElement} node
28535      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28536      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28537      * @param {String} style the style property - eg. text-align
28538      */
28539     getVal : function(node, tag, attr, style)
28540     {
28541         var n = node;
28542         if (tag !== true && n.tagName != tag.toUpperCase()) {
28543             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28544             // but kiss for now.
28545             n = node.getElementsByTagName(tag).item(0);
28546         }
28547         if (!n) {
28548             return '';
28549         }
28550         if (attr === false) {
28551             return n;
28552         }
28553         if (attr == 'html') {
28554             return n.innerHTML;
28555         }
28556         if (attr == 'style') {
28557             return n.style[style]; 
28558         }
28559         
28560         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28561             
28562     },
28563     /**
28564      * create a DomHelper friendly object - for use with 
28565      * Roo.DomHelper.markup / overwrite / etc..
28566      * (override this)
28567      */
28568     toObject : function()
28569     {
28570         return {};
28571     },
28572       /**
28573      * Read a node that has a 'data-block' property - and extract the values from it.
28574      * @param {DomElement} node - the node
28575      */
28576     readElement : function(node)
28577     {
28578         
28579     } 
28580     
28581     
28582 };
28583
28584  
28585
28586 /**
28587  * @class Roo.htmleditor.BlockFigure
28588  * Block that has an image and a figcaption
28589  * @cfg {String} image_src the url for the image
28590  * @cfg {String} align (left|right) alignment for the block default left
28591  * @cfg {String} caption the text to appear below  (and in the alt tag)
28592  * @cfg {String} caption_display (block|none) display or not the caption
28593  * @cfg {String|number} image_width the width of the image number or %?
28594  * @cfg {String|number} image_height the height of the image number or %?
28595  * 
28596  * @constructor
28597  * Create a new Filter.
28598  * @param {Object} config Configuration options
28599  */
28600
28601 Roo.htmleditor.BlockFigure = function(cfg)
28602 {
28603     if (cfg.node) {
28604         this.readElement(cfg.node);
28605         this.updateElement(cfg.node);
28606     }
28607     Roo.apply(this, cfg);
28608 }
28609 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28610  
28611     
28612     // setable values.
28613     image_src: '',
28614     align: 'center',
28615     caption : '',
28616     caption_display : 'block',
28617     width : '100%',
28618     cls : '',
28619     href: '',
28620     video_url : '',
28621     
28622     // margin: '2%', not used
28623     
28624     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28625
28626     
28627     // used by context menu
28628     friendly_name : 'Image with caption',
28629     deleteTitle : "Delete Image and Caption",
28630     
28631     contextMenu : function(toolbar)
28632     {
28633         
28634         var block = function() {
28635             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28636         };
28637         
28638         
28639         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28640         
28641         var syncValue = toolbar.editorcore.syncValue;
28642         
28643         var fields = {};
28644         
28645         return [
28646              {
28647                 xtype : 'TextItem',
28648                 text : "Source: ",
28649                 xns : rooui.Toolbar  //Boostrap?
28650             },
28651             {
28652                 xtype : 'Button',
28653                 text: 'Change Image URL',
28654                  
28655                 listeners : {
28656                     click: function (btn, state)
28657                     {
28658                         var b = block();
28659                         
28660                         Roo.MessageBox.show({
28661                             title : "Image Source URL",
28662                             msg : "Enter the url for the image",
28663                             buttons: Roo.MessageBox.OKCANCEL,
28664                             fn: function(btn, val){
28665                                 if (btn != 'ok') {
28666                                     return;
28667                                 }
28668                                 b.image_src = val;
28669                                 b.updateElement();
28670                                 syncValue();
28671                                 toolbar.editorcore.onEditorEvent();
28672                             },
28673                             minWidth:250,
28674                             prompt:true,
28675                             //multiline: multiline,
28676                             modal : true,
28677                             value : b.image_src
28678                         });
28679                     }
28680                 },
28681                 xns : rooui.Toolbar
28682             },
28683          
28684             {
28685                 xtype : 'Button',
28686                 text: 'Change Link URL',
28687                  
28688                 listeners : {
28689                     click: function (btn, state)
28690                     {
28691                         var b = block();
28692                         
28693                         Roo.MessageBox.show({
28694                             title : "Link URL",
28695                             msg : "Enter the url for the link - leave blank to have no link",
28696                             buttons: Roo.MessageBox.OKCANCEL,
28697                             fn: function(btn, val){
28698                                 if (btn != 'ok') {
28699                                     return;
28700                                 }
28701                                 b.href = val;
28702                                 b.updateElement();
28703                                 syncValue();
28704                                 toolbar.editorcore.onEditorEvent();
28705                             },
28706                             minWidth:250,
28707                             prompt:true,
28708                             //multiline: multiline,
28709                             modal : true,
28710                             value : b.href
28711                         });
28712                     }
28713                 },
28714                 xns : rooui.Toolbar
28715             },
28716             {
28717                 xtype : 'Button',
28718                 text: 'Show Video URL',
28719                  
28720                 listeners : {
28721                     click: function (btn, state)
28722                     {
28723                         Roo.MessageBox.alert("Video URL",
28724                             block().video_url == '' ? 'This image is not linked ot a video' :
28725                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28726                     }
28727                 },
28728                 xns : rooui.Toolbar
28729             },
28730             
28731             
28732             {
28733                 xtype : 'TextItem',
28734                 text : "Width: ",
28735                 xns : rooui.Toolbar  //Boostrap?
28736             },
28737             {
28738                 xtype : 'ComboBox',
28739                 allowBlank : false,
28740                 displayField : 'val',
28741                 editable : true,
28742                 listWidth : 100,
28743                 triggerAction : 'all',
28744                 typeAhead : true,
28745                 valueField : 'val',
28746                 width : 70,
28747                 name : 'width',
28748                 listeners : {
28749                     select : function (combo, r, index)
28750                     {
28751                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28752                         var b = block();
28753                         b.width = r.get('val');
28754                         b.updateElement();
28755                         syncValue();
28756                         toolbar.editorcore.onEditorEvent();
28757                     }
28758                 },
28759                 xns : rooui.form,
28760                 store : {
28761                     xtype : 'SimpleStore',
28762                     data : [
28763                         ['100%'],
28764                         ['80%'],
28765                         ['50%'],
28766                         ['20%'],
28767                         ['10%']
28768                     ],
28769                     fields : [ 'val'],
28770                     xns : Roo.data
28771                 }
28772             },
28773             {
28774                 xtype : 'TextItem',
28775                 text : "Align: ",
28776                 xns : rooui.Toolbar  //Boostrap?
28777             },
28778             {
28779                 xtype : 'ComboBox',
28780                 allowBlank : false,
28781                 displayField : 'val',
28782                 editable : true,
28783                 listWidth : 100,
28784                 triggerAction : 'all',
28785                 typeAhead : true,
28786                 valueField : 'val',
28787                 width : 70,
28788                 name : 'align',
28789                 listeners : {
28790                     select : function (combo, r, index)
28791                     {
28792                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28793                         var b = block();
28794                         b.align = r.get('val');
28795                         b.updateElement();
28796                         syncValue();
28797                         toolbar.editorcore.onEditorEvent();
28798                     }
28799                 },
28800                 xns : rooui.form,
28801                 store : {
28802                     xtype : 'SimpleStore',
28803                     data : [
28804                         ['left'],
28805                         ['right'],
28806                         ['center']
28807                     ],
28808                     fields : [ 'val'],
28809                     xns : Roo.data
28810                 }
28811             },
28812             
28813             
28814             {
28815                 xtype : 'Button',
28816                 text: 'Hide Caption',
28817                 name : 'caption_display',
28818                 pressed : false,
28819                 enableToggle : true,
28820                 setValue : function(v) {
28821                     // this trigger toggle.
28822                      
28823                     this.setText(v ? "Hide Caption" : "Show Caption");
28824                     this.setPressed(v != 'block');
28825                 },
28826                 listeners : {
28827                     toggle: function (btn, state)
28828                     {
28829                         var b  = block();
28830                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28831                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28832                         b.updateElement();
28833                         syncValue();
28834                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28835                         toolbar.editorcore.onEditorEvent();
28836                     }
28837                 },
28838                 xns : rooui.Toolbar
28839             }
28840         ];
28841         
28842     },
28843     /**
28844      * create a DomHelper friendly object - for use with
28845      * Roo.DomHelper.markup / overwrite / etc..
28846      */
28847     toObject : function()
28848     {
28849         var d = document.createElement('div');
28850         d.innerHTML = this.caption;
28851         
28852         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28853         
28854         var iw = this.align == 'center' ? this.width : '100%';
28855         var img =   {
28856             tag : 'img',
28857             contenteditable : 'false',
28858             src : this.image_src,
28859             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28860             style: {
28861                 width : iw,
28862                 maxWidth : iw + ' !important', // this is not getting rendered?
28863                 margin : m  
28864                 
28865             }
28866         };
28867         /*
28868         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28869                     '<a href="{2}">' + 
28870                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28871                     '</a>' + 
28872                 '</div>',
28873         */
28874                 
28875         if (this.href.length > 0) {
28876             img = {
28877                 tag : 'a',
28878                 href: this.href,
28879                 contenteditable : 'true',
28880                 cn : [
28881                     img
28882                 ]
28883             };
28884         }
28885         
28886         
28887         if (this.video_url.length > 0) {
28888             img = {
28889                 tag : 'div',
28890                 cls : this.cls,
28891                 frameborder : 0,
28892                 allowfullscreen : true,
28893                 width : 420,  // these are for video tricks - that we replace the outer
28894                 height : 315,
28895                 src : this.video_url,
28896                 cn : [
28897                     img
28898                 ]
28899             };
28900         }
28901         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28902         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28903         
28904   
28905         var ret =   {
28906             tag: 'figure',
28907             'data-block' : 'Figure',
28908             'data-width' : this.width, 
28909             contenteditable : 'false',
28910             
28911             style : {
28912                 display: 'block',
28913                 float :  this.align ,
28914                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28915                 width : this.align == 'center' ? '100%' : this.width,
28916                 margin:  '0px',
28917                 padding: this.align == 'center' ? '0' : '0 10px' ,
28918                 textAlign : this.align   // seems to work for email..
28919                 
28920             },
28921            
28922             
28923             align : this.align,
28924             cn : [
28925                 img,
28926               
28927                 {
28928                     tag: 'figcaption',
28929                     'data-display' : this.caption_display,
28930                     style : {
28931                         textAlign : 'left',
28932                         fontSize : '16px',
28933                         lineHeight : '24px',
28934                         display : this.caption_display,
28935                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28936                         margin: m,
28937                         width: this.align == 'center' ?  this.width : '100%' 
28938                     
28939                          
28940                     },
28941                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28942                     cn : [
28943                         {
28944                             tag: 'div',
28945                             style  : {
28946                                 marginTop : '16px',
28947                                 textAlign : 'left'
28948                             },
28949                             align: 'left',
28950                             cn : [
28951                                 {
28952                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28953                                     tag : 'i',
28954                                     contenteditable : true,
28955                                     html : captionhtml
28956                                 }
28957                                 
28958                             ]
28959                         }
28960                         
28961                     ]
28962                     
28963                 }
28964             ]
28965         };
28966         return ret;
28967          
28968     },
28969     
28970     readElement : function(node)
28971     {
28972         // this should not really come from the link...
28973         this.video_url = this.getVal(node, 'div', 'src');
28974         this.cls = this.getVal(node, 'div', 'class');
28975         this.href = this.getVal(node, 'a', 'href');
28976         
28977         
28978         this.image_src = this.getVal(node, 'img', 'src');
28979          
28980         this.align = this.getVal(node, 'figure', 'align');
28981         var figcaption = this.getVal(node, 'figcaption', false);
28982         if (figcaption !== '') {
28983             this.caption = this.getVal(figcaption, 'i', 'html');
28984         }
28985         
28986
28987         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28988         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28989         this.width = this.getVal(node, true, 'data-width');
28990         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28991         
28992     },
28993     removeNode : function()
28994     {
28995         return this.node;
28996     }
28997     
28998   
28999    
29000      
29001     
29002     
29003     
29004     
29005 })
29006
29007  
29008
29009 /**
29010  * @class Roo.htmleditor.BlockTable
29011  * Block that manages a table
29012  * 
29013  * @constructor
29014  * Create a new Filter.
29015  * @param {Object} config Configuration options
29016  */
29017
29018 Roo.htmleditor.BlockTable = function(cfg)
29019 {
29020     if (cfg.node) {
29021         this.readElement(cfg.node);
29022         this.updateElement(cfg.node);
29023     }
29024     Roo.apply(this, cfg);
29025     if (!cfg.node) {
29026         this.rows = [];
29027         for(var r = 0; r < this.no_row; r++) {
29028             this.rows[r] = [];
29029             for(var c = 0; c < this.no_col; c++) {
29030                 this.rows[r][c] = this.emptyCell();
29031             }
29032         }
29033     }
29034     
29035     
29036 }
29037 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29038  
29039     rows : false,
29040     no_col : 1,
29041     no_row : 1,
29042     
29043     
29044     width: '100%',
29045     
29046     // used by context menu
29047     friendly_name : 'Table',
29048     deleteTitle : 'Delete Table',
29049     // context menu is drawn once..
29050     
29051     contextMenu : function(toolbar)
29052     {
29053         
29054         var block = function() {
29055             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29056         };
29057         
29058         
29059         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29060         
29061         var syncValue = toolbar.editorcore.syncValue;
29062         
29063         var fields = {};
29064         
29065         return [
29066             {
29067                 xtype : 'TextItem',
29068                 text : "Width: ",
29069                 xns : rooui.Toolbar  //Boostrap?
29070             },
29071             {
29072                 xtype : 'ComboBox',
29073                 allowBlank : false,
29074                 displayField : 'val',
29075                 editable : true,
29076                 listWidth : 100,
29077                 triggerAction : 'all',
29078                 typeAhead : true,
29079                 valueField : 'val',
29080                 width : 100,
29081                 name : 'width',
29082                 listeners : {
29083                     select : function (combo, r, index)
29084                     {
29085                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29086                         var b = block();
29087                         b.width = r.get('val');
29088                         b.updateElement();
29089                         syncValue();
29090                         toolbar.editorcore.onEditorEvent();
29091                     }
29092                 },
29093                 xns : rooui.form,
29094                 store : {
29095                     xtype : 'SimpleStore',
29096                     data : [
29097                         ['100%'],
29098                         ['auto']
29099                     ],
29100                     fields : [ 'val'],
29101                     xns : Roo.data
29102                 }
29103             },
29104             // -------- Cols
29105             
29106             {
29107                 xtype : 'TextItem',
29108                 text : "Columns: ",
29109                 xns : rooui.Toolbar  //Boostrap?
29110             },
29111          
29112             {
29113                 xtype : 'Button',
29114                 text: '-',
29115                 listeners : {
29116                     click : function (_self, e)
29117                     {
29118                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29119                         block().removeColumn();
29120                         syncValue();
29121                         toolbar.editorcore.onEditorEvent();
29122                     }
29123                 },
29124                 xns : rooui.Toolbar
29125             },
29126             {
29127                 xtype : 'Button',
29128                 text: '+',
29129                 listeners : {
29130                     click : function (_self, e)
29131                     {
29132                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29133                         block().addColumn();
29134                         syncValue();
29135                         toolbar.editorcore.onEditorEvent();
29136                     }
29137                 },
29138                 xns : rooui.Toolbar
29139             },
29140             // -------- ROWS
29141             {
29142                 xtype : 'TextItem',
29143                 text : "Rows: ",
29144                 xns : rooui.Toolbar  //Boostrap?
29145             },
29146          
29147             {
29148                 xtype : 'Button',
29149                 text: '-',
29150                 listeners : {
29151                     click : function (_self, e)
29152                     {
29153                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29154                         block().removeRow();
29155                         syncValue();
29156                         toolbar.editorcore.onEditorEvent();
29157                     }
29158                 },
29159                 xns : rooui.Toolbar
29160             },
29161             {
29162                 xtype : 'Button',
29163                 text: '+',
29164                 listeners : {
29165                     click : function (_self, e)
29166                     {
29167                         block().addRow();
29168                         syncValue();
29169                         toolbar.editorcore.onEditorEvent();
29170                     }
29171                 },
29172                 xns : rooui.Toolbar
29173             },
29174             // -------- ROWS
29175             {
29176                 xtype : 'Button',
29177                 text: 'Reset Column Widths',
29178                 listeners : {
29179                     
29180                     click : function (_self, e)
29181                     {
29182                         block().resetWidths();
29183                         syncValue();
29184                         toolbar.editorcore.onEditorEvent();
29185                     }
29186                 },
29187                 xns : rooui.Toolbar
29188             } 
29189             
29190             
29191             
29192         ];
29193         
29194     },
29195     
29196     
29197   /**
29198      * create a DomHelper friendly object - for use with
29199      * Roo.DomHelper.markup / overwrite / etc..
29200      * ?? should it be called with option to hide all editing features?
29201      */
29202     toObject : function()
29203     {
29204         
29205         var ret = {
29206             tag : 'table',
29207             contenteditable : 'false', // this stops cell selection from picking the table.
29208             'data-block' : 'Table',
29209             style : {
29210                 width:  this.width,
29211                 border : 'solid 1px #000', // ??? hard coded?
29212                 'border-collapse' : 'collapse' 
29213             },
29214             cn : [
29215                 { tag : 'tbody' , cn : [] }
29216             ]
29217         };
29218         
29219         // do we have a head = not really 
29220         var ncols = 0;
29221         Roo.each(this.rows, function( row ) {
29222             var tr = {
29223                 tag: 'tr',
29224                 style : {
29225                     margin: '6px',
29226                     border : 'solid 1px #000',
29227                     textAlign : 'left' 
29228                 },
29229                 cn : [ ]
29230             };
29231             
29232             ret.cn[0].cn.push(tr);
29233             // does the row have any properties? ?? height?
29234             var nc = 0;
29235             Roo.each(row, function( cell ) {
29236                 
29237                 var td = {
29238                     tag : 'td',
29239                     contenteditable :  'true',
29240                     'data-block' : 'Td',
29241                     html : cell.html,
29242                     style : cell.style
29243                 };
29244                 if (cell.colspan > 1) {
29245                     td.colspan = cell.colspan ;
29246                     nc += cell.colspan;
29247                 } else {
29248                     nc++;
29249                 }
29250                 if (cell.rowspan > 1) {
29251                     td.rowspan = cell.rowspan ;
29252                 }
29253                 
29254                 
29255                 // widths ?
29256                 tr.cn.push(td);
29257                     
29258                 
29259             }, this);
29260             ncols = Math.max(nc, ncols);
29261             
29262             
29263         }, this);
29264         // add the header row..
29265         
29266         ncols++;
29267          
29268         
29269         return ret;
29270          
29271     },
29272     
29273     readElement : function(node)
29274     {
29275         node  = node ? node : this.node ;
29276         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29277         
29278         this.rows = [];
29279         this.no_row = 0;
29280         var trs = Array.from(node.rows);
29281         trs.forEach(function(tr) {
29282             var row =  [];
29283             this.rows.push(row);
29284             
29285             this.no_row++;
29286             var no_column = 0;
29287             Array.from(tr.cells).forEach(function(td) {
29288                 
29289                 var add = {
29290                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29291                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29292                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29293                     html : td.innerHTML
29294                 };
29295                 no_column += add.colspan;
29296                      
29297                 
29298                 row.push(add);
29299                 
29300                 
29301             },this);
29302             this.no_col = Math.max(this.no_col, no_column);
29303             
29304             
29305         },this);
29306         
29307         
29308     },
29309     normalizeRows: function()
29310     {
29311         var ret= [];
29312         var rid = -1;
29313         this.rows.forEach(function(row) {
29314             rid++;
29315             ret[rid] = [];
29316             row = this.normalizeRow(row);
29317             var cid = 0;
29318             row.forEach(function(c) {
29319                 while (typeof(ret[rid][cid]) != 'undefined') {
29320                     cid++;
29321                 }
29322                 if (typeof(ret[rid]) == 'undefined') {
29323                     ret[rid] = [];
29324                 }
29325                 ret[rid][cid] = c;
29326                 c.row = rid;
29327                 c.col = cid;
29328                 if (c.rowspan < 2) {
29329                     return;
29330                 }
29331                 
29332                 for(var i = 1 ;i < c.rowspan; i++) {
29333                     if (typeof(ret[rid+i]) == 'undefined') {
29334                         ret[rid+i] = [];
29335                     }
29336                     ret[rid+i][cid] = c;
29337                 }
29338             });
29339         }, this);
29340         return ret;
29341     
29342     },
29343     
29344     normalizeRow: function(row)
29345     {
29346         var ret= [];
29347         row.forEach(function(c) {
29348             if (c.colspan < 2) {
29349                 ret.push(c);
29350                 return;
29351             }
29352             for(var i =0 ;i < c.colspan; i++) {
29353                 ret.push(c);
29354             }
29355         });
29356         return ret;
29357     
29358     },
29359     
29360     deleteColumn : function(sel)
29361     {
29362         if (!sel || sel.type != 'col') {
29363             return;
29364         }
29365         if (this.no_col < 2) {
29366             return;
29367         }
29368         
29369         this.rows.forEach(function(row) {
29370             var cols = this.normalizeRow(row);
29371             var col = cols[sel.col];
29372             if (col.colspan > 1) {
29373                 col.colspan --;
29374             } else {
29375                 row.remove(col);
29376             }
29377             
29378         }, this);
29379         this.no_col--;
29380         
29381     },
29382     removeColumn : function()
29383     {
29384         this.deleteColumn({
29385             type: 'col',
29386             col : this.no_col-1
29387         });
29388         this.updateElement();
29389     },
29390     
29391      
29392     addColumn : function()
29393     {
29394         
29395         this.rows.forEach(function(row) {
29396             row.push(this.emptyCell());
29397            
29398         }, this);
29399         this.updateElement();
29400     },
29401     
29402     deleteRow : function(sel)
29403     {
29404         if (!sel || sel.type != 'row') {
29405             return;
29406         }
29407         
29408         if (this.no_row < 2) {
29409             return;
29410         }
29411         
29412         var rows = this.normalizeRows();
29413         
29414         
29415         rows[sel.row].forEach(function(col) {
29416             if (col.rowspan > 1) {
29417                 col.rowspan--;
29418             } else {
29419                 col.remove = 1; // flage it as removed.
29420             }
29421             
29422         }, this);
29423         var newrows = [];
29424         this.rows.forEach(function(row) {
29425             newrow = [];
29426             row.forEach(function(c) {
29427                 if (typeof(c.remove) == 'undefined') {
29428                     newrow.push(c);
29429                 }
29430                 
29431             });
29432             if (newrow.length > 0) {
29433                 newrows.push(row);
29434             }
29435         });
29436         this.rows =  newrows;
29437         
29438         
29439         
29440         this.no_row--;
29441         this.updateElement();
29442         
29443     },
29444     removeRow : function()
29445     {
29446         this.deleteRow({
29447             type: 'row',
29448             row : this.no_row-1
29449         });
29450         
29451     },
29452     
29453      
29454     addRow : function()
29455     {
29456         
29457         var row = [];
29458         for (var i = 0; i < this.no_col; i++ ) {
29459             
29460             row.push(this.emptyCell());
29461            
29462         }
29463         this.rows.push(row);
29464         this.updateElement();
29465         
29466     },
29467      
29468     // the default cell object... at present...
29469     emptyCell : function() {
29470         return (new Roo.htmleditor.BlockTd({})).toObject();
29471         
29472      
29473     },
29474     
29475     removeNode : function()
29476     {
29477         return this.node;
29478     },
29479     
29480     
29481     
29482     resetWidths : function()
29483     {
29484         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29485             var nn = Roo.htmleditor.Block.factory(n);
29486             nn.width = '';
29487             nn.updateElement(n);
29488         });
29489     }
29490     
29491     
29492     
29493     
29494 })
29495
29496 /**
29497  *
29498  * editing a TD?
29499  *
29500  * since selections really work on the table cell, then editing really should work from there
29501  *
29502  * The original plan was to support merging etc... - but that may not be needed yet..
29503  *
29504  * So this simple version will support:
29505  *   add/remove cols
29506  *   adjust the width +/-
29507  *   reset the width...
29508  *   
29509  *
29510  */
29511
29512
29513  
29514
29515 /**
29516  * @class Roo.htmleditor.BlockTable
29517  * Block that manages a table
29518  * 
29519  * @constructor
29520  * Create a new Filter.
29521  * @param {Object} config Configuration options
29522  */
29523
29524 Roo.htmleditor.BlockTd = function(cfg)
29525 {
29526     if (cfg.node) {
29527         this.readElement(cfg.node);
29528         this.updateElement(cfg.node);
29529     }
29530     Roo.apply(this, cfg);
29531      
29532     
29533     
29534 }
29535 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29536  
29537     node : false,
29538     
29539     width: '',
29540     textAlign : 'left',
29541     valign : 'top',
29542     
29543     colspan : 1,
29544     rowspan : 1,
29545     
29546     
29547     // used by context menu
29548     friendly_name : 'Table Cell',
29549     deleteTitle : false, // use our customer delete
29550     
29551     // context menu is drawn once..
29552     
29553     contextMenu : function(toolbar)
29554     {
29555         
29556         var cell = function() {
29557             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29558         };
29559         
29560         var table = function() {
29561             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29562         };
29563         
29564         var lr = false;
29565         var saveSel = function()
29566         {
29567             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29568         }
29569         var restoreSel = function()
29570         {
29571             if (lr) {
29572                 (function() {
29573                     toolbar.editorcore.focus();
29574                     var cr = toolbar.editorcore.getSelection();
29575                     cr.removeAllRanges();
29576                     cr.addRange(lr);
29577                     toolbar.editorcore.onEditorEvent();
29578                 }).defer(10, this);
29579                 
29580                 
29581             }
29582         }
29583         
29584         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29585         
29586         var syncValue = toolbar.editorcore.syncValue;
29587         
29588         var fields = {};
29589         
29590         return [
29591             {
29592                 xtype : 'Button',
29593                 text : 'Edit Table',
29594                 listeners : {
29595                     click : function() {
29596                         var t = toolbar.tb.selectedNode.closest('table');
29597                         toolbar.editorcore.selectNode(t);
29598                         toolbar.editorcore.onEditorEvent();                        
29599                     }
29600                 }
29601                 
29602             },
29603               
29604            
29605              
29606             {
29607                 xtype : 'TextItem',
29608                 text : "Column Width: ",
29609                  xns : rooui.Toolbar 
29610                
29611             },
29612             {
29613                 xtype : 'Button',
29614                 text: '-',
29615                 listeners : {
29616                     click : function (_self, e)
29617                     {
29618                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29619                         cell().shrinkColumn();
29620                         syncValue();
29621                          toolbar.editorcore.onEditorEvent();
29622                     }
29623                 },
29624                 xns : rooui.Toolbar
29625             },
29626             {
29627                 xtype : 'Button',
29628                 text: '+',
29629                 listeners : {
29630                     click : function (_self, e)
29631                     {
29632                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29633                         cell().growColumn();
29634                         syncValue();
29635                         toolbar.editorcore.onEditorEvent();
29636                     }
29637                 },
29638                 xns : rooui.Toolbar
29639             },
29640             
29641             {
29642                 xtype : 'TextItem',
29643                 text : "Vertical Align: ",
29644                 xns : rooui.Toolbar  //Boostrap?
29645             },
29646             {
29647                 xtype : 'ComboBox',
29648                 allowBlank : false,
29649                 displayField : 'val',
29650                 editable : true,
29651                 listWidth : 100,
29652                 triggerAction : 'all',
29653                 typeAhead : true,
29654                 valueField : 'val',
29655                 width : 100,
29656                 name : 'valign',
29657                 listeners : {
29658                     select : function (combo, r, index)
29659                     {
29660                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29661                         var b = cell();
29662                         b.valign = r.get('val');
29663                         b.updateElement();
29664                         syncValue();
29665                         toolbar.editorcore.onEditorEvent();
29666                     }
29667                 },
29668                 xns : rooui.form,
29669                 store : {
29670                     xtype : 'SimpleStore',
29671                     data : [
29672                         ['top'],
29673                         ['middle'],
29674                         ['bottom'] // there are afew more... 
29675                     ],
29676                     fields : [ 'val'],
29677                     xns : Roo.data
29678                 }
29679             },
29680             
29681             {
29682                 xtype : 'TextItem',
29683                 text : "Merge Cells: ",
29684                  xns : rooui.Toolbar 
29685                
29686             },
29687             
29688             
29689             {
29690                 xtype : 'Button',
29691                 text: 'Right',
29692                 listeners : {
29693                     click : function (_self, e)
29694                     {
29695                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29696                         cell().mergeRight();
29697                         //block().growColumn();
29698                         syncValue();
29699                         toolbar.editorcore.onEditorEvent();
29700                     }
29701                 },
29702                 xns : rooui.Toolbar
29703             },
29704              
29705             {
29706                 xtype : 'Button',
29707                 text: 'Below',
29708                 listeners : {
29709                     click : function (_self, e)
29710                     {
29711                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29712                         cell().mergeBelow();
29713                         //block().growColumn();
29714                         syncValue();
29715                         toolbar.editorcore.onEditorEvent();
29716                     }
29717                 },
29718                 xns : rooui.Toolbar
29719             },
29720             {
29721                 xtype : 'TextItem',
29722                 text : "| ",
29723                  xns : rooui.Toolbar 
29724                
29725             },
29726             
29727             {
29728                 xtype : 'Button',
29729                 text: 'Split',
29730                 listeners : {
29731                     click : function (_self, e)
29732                     {
29733                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29734                         cell().split();
29735                         syncValue();
29736                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29737                         toolbar.editorcore.onEditorEvent();
29738                                              
29739                     }
29740                 },
29741                 xns : rooui.Toolbar
29742             },
29743             {
29744                 xtype : 'Fill',
29745                 xns : rooui.Toolbar 
29746                
29747             },
29748         
29749           
29750             {
29751                 xtype : 'Button',
29752                 text: 'Delete',
29753                  
29754                 xns : rooui.Toolbar,
29755                 menu : {
29756                     xtype : 'Menu',
29757                     xns : rooui.menu,
29758                     items : [
29759                         {
29760                             xtype : 'Item',
29761                             html: 'Column',
29762                             listeners : {
29763                                 click : function (_self, e)
29764                                 {
29765                                     var t = table();
29766                                     
29767                                     cell().deleteColumn();
29768                                     syncValue();
29769                                     toolbar.editorcore.selectNode(t.node);
29770                                     toolbar.editorcore.onEditorEvent();   
29771                                 }
29772                             },
29773                             xns : rooui.menu
29774                         },
29775                         {
29776                             xtype : 'Item',
29777                             html: 'Row',
29778                             listeners : {
29779                                 click : function (_self, e)
29780                                 {
29781                                     var t = table();
29782                                     cell().deleteRow();
29783                                     syncValue();
29784                                     
29785                                     toolbar.editorcore.selectNode(t.node);
29786                                     toolbar.editorcore.onEditorEvent();   
29787                                                          
29788                                 }
29789                             },
29790                             xns : rooui.menu
29791                         },
29792                        {
29793                             xtype : 'Separator',
29794                             xns : rooui.menu
29795                         },
29796                         {
29797                             xtype : 'Item',
29798                             html: 'Table',
29799                             listeners : {
29800                                 click : function (_self, e)
29801                                 {
29802                                     var t = table();
29803                                     var nn = t.node.nextSibling || t.node.previousSibling;
29804                                     t.node.parentNode.removeChild(t.node);
29805                                     if (nn) { 
29806                                         toolbar.editorcore.selectNode(nn, true);
29807                                     }
29808                                     toolbar.editorcore.onEditorEvent();   
29809                                                          
29810                                 }
29811                             },
29812                             xns : rooui.menu
29813                         }
29814                     ]
29815                 }
29816             }
29817             
29818             // align... << fixme
29819             
29820         ];
29821         
29822     },
29823     
29824     
29825   /**
29826      * create a DomHelper friendly object - for use with
29827      * Roo.DomHelper.markup / overwrite / etc..
29828      * ?? should it be called with option to hide all editing features?
29829      */
29830  /**
29831      * create a DomHelper friendly object - for use with
29832      * Roo.DomHelper.markup / overwrite / etc..
29833      * ?? should it be called with option to hide all editing features?
29834      */
29835     toObject : function()
29836     {
29837         var ret = {
29838             tag : 'td',
29839             contenteditable : 'true', // this stops cell selection from picking the table.
29840             'data-block' : 'Td',
29841             valign : this.valign,
29842             style : {  
29843                 'text-align' :  this.textAlign,
29844                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29845                 'border-collapse' : 'collapse',
29846                 padding : '6px', // 8 for desktop / 4 for mobile
29847                 'vertical-align': this.valign
29848             },
29849             html : this.html
29850         };
29851         if (this.width != '') {
29852             ret.width = this.width;
29853             ret.style.width = this.width;
29854         }
29855         
29856         
29857         if (this.colspan > 1) {
29858             ret.colspan = this.colspan ;
29859         } 
29860         if (this.rowspan > 1) {
29861             ret.rowspan = this.rowspan ;
29862         }
29863         
29864            
29865         
29866         return ret;
29867          
29868     },
29869     
29870     readElement : function(node)
29871     {
29872         node  = node ? node : this.node ;
29873         this.width = node.style.width;
29874         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29875         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29876         this.html = node.innerHTML;
29877         if (node.style.textAlign != '') {
29878             this.textAlign = node.style.textAlign;
29879         }
29880         
29881         
29882     },
29883      
29884     // the default cell object... at present...
29885     emptyCell : function() {
29886         return {
29887             colspan :  1,
29888             rowspan :  1,
29889             textAlign : 'left',
29890             html : "&nbsp;" // is this going to be editable now?
29891         };
29892      
29893     },
29894     
29895     removeNode : function()
29896     {
29897         return this.node.closest('table');
29898          
29899     },
29900     
29901     cellData : false,
29902     
29903     colWidths : false,
29904     
29905     toTableArray  : function()
29906     {
29907         var ret = [];
29908         var tab = this.node.closest('tr').closest('table');
29909         Array.from(tab.rows).forEach(function(r, ri){
29910             ret[ri] = [];
29911         });
29912         var rn = 0;
29913         this.colWidths = [];
29914         var all_auto = true;
29915         Array.from(tab.rows).forEach(function(r, ri){
29916             
29917             var cn = 0;
29918             Array.from(r.cells).forEach(function(ce, ci){
29919                 var c =  {
29920                     cell : ce,
29921                     row : rn,
29922                     col: cn,
29923                     colspan : ce.colSpan,
29924                     rowspan : ce.rowSpan
29925                 };
29926                 if (ce.isEqualNode(this.node)) {
29927                     this.cellData = c;
29928                 }
29929                 // if we have been filled up by a row?
29930                 if (typeof(ret[rn][cn]) != 'undefined') {
29931                     while(typeof(ret[rn][cn]) != 'undefined') {
29932                         cn++;
29933                     }
29934                     c.col = cn;
29935                 }
29936                 
29937                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29938                     this.colWidths[cn] =   ce.style.width;
29939                     if (this.colWidths[cn] != '') {
29940                         all_auto = false;
29941                     }
29942                 }
29943                 
29944                 
29945                 if (c.colspan < 2 && c.rowspan < 2 ) {
29946                     ret[rn][cn] = c;
29947                     cn++;
29948                     return;
29949                 }
29950                 for(var j = 0; j < c.rowspan; j++) {
29951                     if (typeof(ret[rn+j]) == 'undefined') {
29952                         continue; // we have a problem..
29953                     }
29954                     ret[rn+j][cn] = c;
29955                     for(var i = 0; i < c.colspan; i++) {
29956                         ret[rn+j][cn+i] = c;
29957                     }
29958                 }
29959                 
29960                 cn += c.colspan;
29961             }, this);
29962             rn++;
29963         }, this);
29964         
29965         // initalize widths.?
29966         // either all widths or no widths..
29967         if (all_auto) {
29968             this.colWidths[0] = false; // no widths flag.
29969         }
29970         
29971         
29972         return ret;
29973         
29974     },
29975     
29976     
29977     
29978     
29979     mergeRight: function()
29980     {
29981          
29982         // get the contents of the next cell along..
29983         var tr = this.node.closest('tr');
29984         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29985         if (i >= tr.childNodes.length - 1) {
29986             return; // no cells on right to merge with.
29987         }
29988         var table = this.toTableArray();
29989         
29990         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29991             return; // nothing right?
29992         }
29993         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29994         // right cell - must be same rowspan and on the same row.
29995         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29996             return; // right hand side is not same rowspan.
29997         }
29998         
29999         
30000         
30001         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30002         tr.removeChild(rc.cell);
30003         this.colspan += rc.colspan;
30004         this.node.setAttribute('colspan', this.colspan);
30005
30006         var table = this.toTableArray();
30007         this.normalizeWidths(table);
30008         this.updateWidths(table);
30009     },
30010     
30011     
30012     mergeBelow : function()
30013     {
30014         var table = this.toTableArray();
30015         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30016             return; // no row below
30017         }
30018         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30019             return; // nothing right?
30020         }
30021         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30022         
30023         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30024             return; // right hand side is not same rowspan.
30025         }
30026         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30027         rc.cell.parentNode.removeChild(rc.cell);
30028         this.rowspan += rc.rowspan;
30029         this.node.setAttribute('rowspan', this.rowspan);
30030     },
30031     
30032     split: function()
30033     {
30034         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30035             return;
30036         }
30037         var table = this.toTableArray();
30038         var cd = this.cellData;
30039         this.rowspan = 1;
30040         this.colspan = 1;
30041         
30042         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30043              
30044             
30045             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30046                 if (r == cd.row && c == cd.col) {
30047                     this.node.removeAttribute('rowspan');
30048                     this.node.removeAttribute('colspan');
30049                 }
30050                  
30051                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30052                 ntd.removeAttribute('id'); 
30053                 ntd.style.width  = this.colWidths[c];
30054                 ntd.innerHTML = '';
30055                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30056             }
30057             
30058         }
30059         this.redrawAllCells(table);
30060         
30061     },
30062     
30063     
30064     
30065     redrawAllCells: function(table)
30066     {
30067         
30068          
30069         var tab = this.node.closest('tr').closest('table');
30070         var ctr = tab.rows[0].parentNode;
30071         Array.from(tab.rows).forEach(function(r, ri){
30072             
30073             Array.from(r.cells).forEach(function(ce, ci){
30074                 ce.parentNode.removeChild(ce);
30075             });
30076             r.parentNode.removeChild(r);
30077         });
30078         for(var r = 0 ; r < table.length; r++) {
30079             var re = tab.rows[r];
30080             
30081             var re = tab.ownerDocument.createElement('tr');
30082             ctr.appendChild(re);
30083             for(var c = 0 ; c < table[r].length; c++) {
30084                 if (table[r][c].cell === false) {
30085                     continue;
30086                 }
30087                 
30088                 re.appendChild(table[r][c].cell);
30089                  
30090                 table[r][c].cell = false;
30091             }
30092         }
30093         
30094     },
30095     updateWidths : function(table)
30096     {
30097         for(var r = 0 ; r < table.length; r++) {
30098            
30099             for(var c = 0 ; c < table[r].length; c++) {
30100                 if (table[r][c].cell === false) {
30101                     continue;
30102                 }
30103                 
30104                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30105                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30106                     el.width = Math.floor(this.colWidths[c])  +'%';
30107                     el.updateElement(el.node);
30108                 }
30109                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30110                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30111                     var width = 0;
30112                     for(var i = 0; i < table[r][c].colspan; i ++) {
30113                         width += Math.floor(this.colWidths[c + i]);
30114                     }
30115                     el.width = width  +'%';
30116                     el.updateElement(el.node);
30117                 }
30118                 table[r][c].cell = false; // done
30119             }
30120         }
30121     },
30122     normalizeWidths : function(table)
30123     {
30124         if (this.colWidths[0] === false) {
30125             var nw = 100.0 / this.colWidths.length;
30126             this.colWidths.forEach(function(w,i) {
30127                 this.colWidths[i] = nw;
30128             },this);
30129             return;
30130         }
30131     
30132         var t = 0, missing = [];
30133         
30134         this.colWidths.forEach(function(w,i) {
30135             //if you mix % and
30136             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30137             var add =  this.colWidths[i];
30138             if (add > 0) {
30139                 t+=add;
30140                 return;
30141             }
30142             missing.push(i);
30143             
30144             
30145         },this);
30146         var nc = this.colWidths.length;
30147         if (missing.length) {
30148             var mult = (nc - missing.length) / (1.0 * nc);
30149             var t = mult * t;
30150             var ew = (100 -t) / (1.0 * missing.length);
30151             this.colWidths.forEach(function(w,i) {
30152                 if (w > 0) {
30153                     this.colWidths[i] = w * mult;
30154                     return;
30155                 }
30156                 
30157                 this.colWidths[i] = ew;
30158             }, this);
30159             // have to make up numbers..
30160              
30161         }
30162         // now we should have all the widths..
30163         
30164     
30165     },
30166     
30167     shrinkColumn : function()
30168     {
30169         var table = this.toTableArray();
30170         this.normalizeWidths(table);
30171         var col = this.cellData.col;
30172         var nw = this.colWidths[col] * 0.8;
30173         if (nw < 5) {
30174             return;
30175         }
30176         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30177         this.colWidths.forEach(function(w,i) {
30178             if (i == col) {
30179                  this.colWidths[i] = nw;
30180                 return;
30181             }
30182             this.colWidths[i] += otherAdd
30183         }, this);
30184         this.updateWidths(table);
30185          
30186     },
30187     growColumn : function()
30188     {
30189         var table = this.toTableArray();
30190         this.normalizeWidths(table);
30191         var col = this.cellData.col;
30192         var nw = this.colWidths[col] * 1.2;
30193         if (nw > 90) {
30194             return;
30195         }
30196         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30197         this.colWidths.forEach(function(w,i) {
30198             if (i == col) {
30199                 this.colWidths[i] = nw;
30200                 return;
30201             }
30202             this.colWidths[i] -= otherSub
30203         }, this);
30204         this.updateWidths(table);
30205          
30206     },
30207     deleteRow : function()
30208     {
30209         // delete this rows 'tr'
30210         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30211         // then reduce the rowspan.
30212         var table = this.toTableArray();
30213         // this.cellData.row;
30214         for (var i =0;i< table[this.cellData.row].length ; i++) {
30215             var c = table[this.cellData.row][i];
30216             if (c.row != this.cellData.row) {
30217                 
30218                 c.rowspan--;
30219                 c.cell.setAttribute('rowspan', c.rowspan);
30220                 continue;
30221             }
30222             if (c.rowspan > 1) {
30223                 c.rowspan--;
30224                 c.cell.setAttribute('rowspan', c.rowspan);
30225             }
30226         }
30227         table.splice(this.cellData.row,1);
30228         this.redrawAllCells(table);
30229         
30230     },
30231     deleteColumn : function()
30232     {
30233         var table = this.toTableArray();
30234         
30235         for (var i =0;i< table.length ; i++) {
30236             var c = table[i][this.cellData.col];
30237             if (c.col != this.cellData.col) {
30238                 table[i][this.cellData.col].colspan--;
30239             } else if (c.colspan > 1) {
30240                 c.colspan--;
30241                 c.cell.setAttribute('colspan', c.colspan);
30242             }
30243             table[i].splice(this.cellData.col,1);
30244         }
30245         
30246         this.redrawAllCells(table);
30247     }
30248     
30249     
30250     
30251     
30252 })
30253
30254 //<script type="text/javascript">
30255
30256 /*
30257  * Based  Ext JS Library 1.1.1
30258  * Copyright(c) 2006-2007, Ext JS, LLC.
30259  * LGPL
30260  *
30261  */
30262  
30263 /**
30264  * @class Roo.HtmlEditorCore
30265  * @extends Roo.Component
30266  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30267  *
30268  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30269  */
30270
30271 Roo.HtmlEditorCore = function(config){
30272     
30273     
30274     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30275     
30276     
30277     this.addEvents({
30278         /**
30279          * @event initialize
30280          * Fires when the editor is fully initialized (including the iframe)
30281          * @param {Roo.HtmlEditorCore} this
30282          */
30283         initialize: true,
30284         /**
30285          * @event activate
30286          * Fires when the editor is first receives the focus. Any insertion must wait
30287          * until after this event.
30288          * @param {Roo.HtmlEditorCore} this
30289          */
30290         activate: true,
30291          /**
30292          * @event beforesync
30293          * Fires before the textarea is updated with content from the editor iframe. Return false
30294          * to cancel the sync.
30295          * @param {Roo.HtmlEditorCore} this
30296          * @param {String} html
30297          */
30298         beforesync: true,
30299          /**
30300          * @event beforepush
30301          * Fires before the iframe editor is updated with content from the textarea. Return false
30302          * to cancel the push.
30303          * @param {Roo.HtmlEditorCore} this
30304          * @param {String} html
30305          */
30306         beforepush: true,
30307          /**
30308          * @event sync
30309          * Fires when the textarea is updated with content from the editor iframe.
30310          * @param {Roo.HtmlEditorCore} this
30311          * @param {String} html
30312          */
30313         sync: true,
30314          /**
30315          * @event push
30316          * Fires when the iframe editor is updated with content from the textarea.
30317          * @param {Roo.HtmlEditorCore} this
30318          * @param {String} html
30319          */
30320         push: true,
30321         
30322         /**
30323          * @event editorevent
30324          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30325          * @param {Roo.HtmlEditorCore} this
30326          */
30327         editorevent: true 
30328          
30329         
30330     });
30331     
30332     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30333     
30334     // defaults : white / black...
30335     this.applyBlacklists();
30336     
30337     
30338     
30339 };
30340
30341
30342 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30343
30344
30345      /**
30346      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30347      */
30348     
30349     owner : false,
30350     
30351      /**
30352      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30353      *                        Roo.resizable.
30354      */
30355     resizable : false,
30356      /**
30357      * @cfg {Number} height (in pixels)
30358      */   
30359     height: 300,
30360    /**
30361      * @cfg {Number} width (in pixels)
30362      */   
30363     width: 500,
30364      /**
30365      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30366      *         if you are doing an email editor, this probably needs disabling, it's designed
30367      */
30368     autoClean: true,
30369     
30370     /**
30371      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30372      */
30373     enableBlocks : true,
30374     /**
30375      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30376      * 
30377      */
30378     stylesheets: false,
30379      /**
30380      * @cfg {String} language default en - language of text (usefull for rtl languages)
30381      * 
30382      */
30383     language: 'en',
30384     
30385     /**
30386      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30387      *          - by default they are stripped - if you are editing email you may need this.
30388      */
30389     allowComments: false,
30390     // id of frame..
30391     frameId: false,
30392     
30393     // private properties
30394     validationEvent : false,
30395     deferHeight: true,
30396     initialized : false,
30397     activated : false,
30398     sourceEditMode : false,
30399     onFocus : Roo.emptyFn,
30400     iframePad:3,
30401     hideMode:'offsets',
30402     
30403     clearUp: true,
30404     
30405     // blacklist + whitelisted elements..
30406     black: false,
30407     white: false,
30408      
30409     bodyCls : '',
30410
30411     
30412     undoManager : false,
30413     /**
30414      * Protected method that will not generally be called directly. It
30415      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30416      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30417      */
30418     getDocMarkup : function(){
30419         // body styles..
30420         var st = '';
30421         
30422         // inherit styels from page...?? 
30423         if (this.stylesheets === false) {
30424             
30425             Roo.get(document.head).select('style').each(function(node) {
30426                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30427             });
30428             
30429             Roo.get(document.head).select('link').each(function(node) { 
30430                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30431             });
30432             
30433         } else if (!this.stylesheets.length) {
30434                 // simple..
30435                 st = '<style type="text/css">' +
30436                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30437                    '</style>';
30438         } else {
30439             for (var i in this.stylesheets) {
30440                 if (typeof(this.stylesheets[i]) != 'string') {
30441                     continue;
30442                 }
30443                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30444             }
30445             
30446         }
30447         
30448         st +=  '<style type="text/css">' +
30449             'IMG { cursor: pointer } ' +
30450         '</style>';
30451         
30452         st += '<meta name="google" content="notranslate">';
30453         
30454         var cls = 'notranslate roo-htmleditor-body';
30455         
30456         if(this.bodyCls.length){
30457             cls += ' ' + this.bodyCls;
30458         }
30459         
30460         return '<html  class="notranslate" translate="no"><head>' + st  +
30461             //<style type="text/css">' +
30462             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30463             //'</style>' +
30464             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30465     },
30466
30467     // private
30468     onRender : function(ct, position)
30469     {
30470         var _t = this;
30471         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30472         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30473         
30474         
30475         this.el.dom.style.border = '0 none';
30476         this.el.dom.setAttribute('tabIndex', -1);
30477         this.el.addClass('x-hidden hide');
30478         
30479         
30480         
30481         if(Roo.isIE){ // fix IE 1px bogus margin
30482             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30483         }
30484        
30485         
30486         this.frameId = Roo.id();
30487         
30488          
30489         
30490         var iframe = this.owner.wrap.createChild({
30491             tag: 'iframe',
30492             cls: 'form-control', // bootstrap..
30493             id: this.frameId,
30494             name: this.frameId,
30495             frameBorder : 'no',
30496             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30497         }, this.el
30498         );
30499         
30500         
30501         this.iframe = iframe.dom;
30502
30503         this.assignDocWin();
30504         
30505         this.doc.designMode = 'on';
30506        
30507         this.doc.open();
30508         this.doc.write(this.getDocMarkup());
30509         this.doc.close();
30510
30511         
30512         var task = { // must defer to wait for browser to be ready
30513             run : function(){
30514                 //console.log("run task?" + this.doc.readyState);
30515                 this.assignDocWin();
30516                 if(this.doc.body || this.doc.readyState == 'complete'){
30517                     try {
30518                         this.doc.designMode="on";
30519                         
30520                     } catch (e) {
30521                         return;
30522                     }
30523                     Roo.TaskMgr.stop(task);
30524                     this.initEditor.defer(10, this);
30525                 }
30526             },
30527             interval : 10,
30528             duration: 10000,
30529             scope: this
30530         };
30531         Roo.TaskMgr.start(task);
30532
30533     },
30534
30535     // private
30536     onResize : function(w, h)
30537     {
30538          Roo.log('resize: ' +w + ',' + h );
30539         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30540         if(!this.iframe){
30541             return;
30542         }
30543         if(typeof w == 'number'){
30544             
30545             this.iframe.style.width = w + 'px';
30546         }
30547         if(typeof h == 'number'){
30548             
30549             this.iframe.style.height = h + 'px';
30550             if(this.doc){
30551                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30552             }
30553         }
30554         
30555     },
30556
30557     /**
30558      * Toggles the editor between standard and source edit mode.
30559      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30560      */
30561     toggleSourceEdit : function(sourceEditMode){
30562         
30563         this.sourceEditMode = sourceEditMode === true;
30564         
30565         if(this.sourceEditMode){
30566  
30567             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30568             
30569         }else{
30570             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30571             //this.iframe.className = '';
30572             this.deferFocus();
30573         }
30574         //this.setSize(this.owner.wrap.getSize());
30575         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30576     },
30577
30578     
30579   
30580
30581     /**
30582      * Protected method that will not generally be called directly. If you need/want
30583      * custom HTML cleanup, this is the method you should override.
30584      * @param {String} html The HTML to be cleaned
30585      * return {String} The cleaned HTML
30586      */
30587     cleanHtml : function(html)
30588     {
30589         html = String(html);
30590         if(html.length > 5){
30591             if(Roo.isSafari){ // strip safari nonsense
30592                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30593             }
30594         }
30595         if(html == '&nbsp;'){
30596             html = '';
30597         }
30598         return html;
30599     },
30600
30601     /**
30602      * HTML Editor -> Textarea
30603      * Protected method that will not generally be called directly. Syncs the contents
30604      * of the editor iframe with the textarea.
30605      */
30606     syncValue : function()
30607     {
30608         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30609         if(this.initialized){
30610             
30611             if (this.undoManager) {
30612                 this.undoManager.addEvent();
30613             }
30614
30615             
30616             var bd = (this.doc.body || this.doc.documentElement);
30617            
30618             
30619             var sel = this.win.getSelection();
30620             
30621             var div = document.createElement('div');
30622             div.innerHTML = bd.innerHTML;
30623             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30624             if (gtx.length > 0) {
30625                 var rm = gtx.item(0).parentNode;
30626                 rm.parentNode.removeChild(rm);
30627             }
30628             
30629            
30630             if (this.enableBlocks) {
30631                 new Roo.htmleditor.FilterBlock({ node : div });
30632             }
30633             
30634             var html = div.innerHTML;
30635             
30636             //?? tidy?
30637             if (this.autoClean) {
30638                 
30639                 new Roo.htmleditor.FilterAttributes({
30640                     node : div,
30641                     attrib_white : [
30642                             'href',
30643                             'src',
30644                             'name',
30645                             'align',
30646                             'colspan',
30647                             'rowspan',
30648                             'data-display',
30649                             'data-width',
30650                             'start' ,
30651                             'style',
30652                             // youtube embed.
30653                             'class',
30654                             'allowfullscreen',
30655                             'frameborder',
30656                             'width',
30657                             'height',
30658                             'alt'
30659                             ],
30660                     attrib_clean : ['href', 'src' ] 
30661                 });
30662                 
30663                 var tidy = new Roo.htmleditor.TidySerializer({
30664                     inner:  true
30665                 });
30666                 html  = tidy.serialize(div);
30667                 
30668             }
30669             
30670             
30671             if(Roo.isSafari){
30672                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30673                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30674                 if(m && m[1]){
30675                     html = '<div style="'+m[0]+'">' + html + '</div>';
30676                 }
30677             }
30678             html = this.cleanHtml(html);
30679             // fix up the special chars.. normaly like back quotes in word...
30680             // however we do not want to do this with chinese..
30681             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30682                 
30683                 var cc = match.charCodeAt();
30684
30685                 // Get the character value, handling surrogate pairs
30686                 if (match.length == 2) {
30687                     // It's a surrogate pair, calculate the Unicode code point
30688                     var high = match.charCodeAt(0) - 0xD800;
30689                     var low  = match.charCodeAt(1) - 0xDC00;
30690                     cc = (high * 0x400) + low + 0x10000;
30691                 }  else if (
30692                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30693                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30694                     (cc >= 0xf900 && cc < 0xfb00 )
30695                 ) {
30696                         return match;
30697                 }  
30698          
30699                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30700                 return "&#" + cc + ";";
30701                 
30702                 
30703             });
30704             
30705             
30706              
30707             if(this.owner.fireEvent('beforesync', this, html) !== false){
30708                 this.el.dom.value = html;
30709                 this.owner.fireEvent('sync', this, html);
30710             }
30711         }
30712     },
30713
30714     /**
30715      * TEXTAREA -> EDITABLE
30716      * Protected method that will not generally be called directly. Pushes the value of the textarea
30717      * into the iframe editor.
30718      */
30719     pushValue : function()
30720     {
30721         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30722         if(this.initialized){
30723             var v = this.el.dom.value.trim();
30724             
30725             
30726             if(this.owner.fireEvent('beforepush', this, v) !== false){
30727                 var d = (this.doc.body || this.doc.documentElement);
30728                 d.innerHTML = v;
30729                  
30730                 this.el.dom.value = d.innerHTML;
30731                 this.owner.fireEvent('push', this, v);
30732             }
30733             if (this.autoClean) {
30734                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30735                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30736             }
30737             if (this.enableBlocks) {
30738                 Roo.htmleditor.Block.initAll(this.doc.body);
30739             }
30740             
30741             this.updateLanguage();
30742             
30743             var lc = this.doc.body.lastChild;
30744             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30745                 // add an extra line at the end.
30746                 this.doc.body.appendChild(this.doc.createElement('br'));
30747             }
30748             
30749             
30750         }
30751     },
30752
30753     // private
30754     deferFocus : function(){
30755         this.focus.defer(10, this);
30756     },
30757
30758     // doc'ed in Field
30759     focus : function(){
30760         if(this.win && !this.sourceEditMode){
30761             this.win.focus();
30762         }else{
30763             this.el.focus();
30764         }
30765     },
30766     
30767     assignDocWin: function()
30768     {
30769         var iframe = this.iframe;
30770         
30771          if(Roo.isIE){
30772             this.doc = iframe.contentWindow.document;
30773             this.win = iframe.contentWindow;
30774         } else {
30775 //            if (!Roo.get(this.frameId)) {
30776 //                return;
30777 //            }
30778 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30779 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30780             
30781             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30782                 return;
30783             }
30784             
30785             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30786             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30787         }
30788     },
30789     
30790     // private
30791     initEditor : function(){
30792         //console.log("INIT EDITOR");
30793         this.assignDocWin();
30794         
30795         
30796         
30797         this.doc.designMode="on";
30798         this.doc.open();
30799         this.doc.write(this.getDocMarkup());
30800         this.doc.close();
30801         
30802         var dbody = (this.doc.body || this.doc.documentElement);
30803         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30804         // this copies styles from the containing element into thsi one..
30805         // not sure why we need all of this..
30806         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30807         
30808         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30809         //ss['background-attachment'] = 'fixed'; // w3c
30810         dbody.bgProperties = 'fixed'; // ie
30811         dbody.setAttribute("translate", "no");
30812         
30813         //Roo.DomHelper.applyStyles(dbody, ss);
30814         Roo.EventManager.on(this.doc, {
30815              
30816             'mouseup': this.onEditorEvent,
30817             'dblclick': this.onEditorEvent,
30818             'click': this.onEditorEvent,
30819             'keyup': this.onEditorEvent,
30820             
30821             buffer:100,
30822             scope: this
30823         });
30824         Roo.EventManager.on(this.doc, {
30825             'paste': this.onPasteEvent,
30826             scope : this
30827         });
30828         if(Roo.isGecko){
30829             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30830         }
30831         //??? needed???
30832         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30833             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30834         }
30835         this.initialized = true;
30836
30837         
30838         // initialize special key events - enter
30839         new Roo.htmleditor.KeyEnter({core : this});
30840         
30841          
30842         
30843         this.owner.fireEvent('initialize', this);
30844         this.pushValue();
30845     },
30846     // this is to prevent a href clicks resulting in a redirect?
30847    
30848     onPasteEvent : function(e,v)
30849     {
30850         // I think we better assume paste is going to be a dirty load of rubish from word..
30851         
30852         // even pasting into a 'email version' of this widget will have to clean up that mess.
30853         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30854         
30855         // check what type of paste - if it's an image, then handle it differently.
30856         if (cd.files && cd.files.length > 0) {
30857             // pasting images?
30858             var urlAPI = (window.createObjectURL && window) || 
30859                 (window.URL && URL.revokeObjectURL && URL) || 
30860                 (window.webkitURL && webkitURL);
30861     
30862             var url = urlAPI.createObjectURL( cd.files[0]);
30863             this.insertAtCursor('<img src=" + url + ">');
30864             return false;
30865         }
30866         if (cd.types.indexOf('text/html') < 0 ) {
30867             return false;
30868         }
30869         var images = [];
30870         var html = cd.getData('text/html'); // clipboard event
30871         if (cd.types.indexOf('text/rtf') > -1) {
30872             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30873             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30874         }
30875         //Roo.log(images);
30876         //Roo.log(imgs);
30877         // fixme..
30878         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30879                        .map(function(g) { return g.toDataURL(); })
30880                        .filter(function(g) { return g != 'about:blank'; });
30881         
30882         //Roo.log(html);
30883         html = this.cleanWordChars(html);
30884         
30885         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30886         
30887         
30888         var sn = this.getParentElement();
30889         // check if d contains a table, and prevent nesting??
30890         //Roo.log(d.getElementsByTagName('table'));
30891         //Roo.log(sn);
30892         //Roo.log(sn.closest('table'));
30893         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30894             e.preventDefault();
30895             this.insertAtCursor("You can not nest tables");
30896             //Roo.log("prevent?"); // fixme - 
30897             return false;
30898         }
30899         
30900         
30901         
30902         if (images.length > 0) {
30903             // replace all v:imagedata - with img.
30904             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30905             Roo.each(ar, function(node) {
30906                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30907                 node.parentNode.removeChild(node);
30908             });
30909             
30910             
30911             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30912                 img.setAttribute('src', images[i]);
30913             });
30914         }
30915         if (this.autoClean) {
30916             new Roo.htmleditor.FilterWord({ node : d });
30917             
30918             new Roo.htmleditor.FilterStyleToTag({ node : d });
30919             new Roo.htmleditor.FilterAttributes({
30920                 node : d,
30921                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30922                 attrib_clean : ['href', 'src' ] 
30923             });
30924             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30925             // should be fonts..
30926             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30927             new Roo.htmleditor.FilterParagraph({ node : d });
30928             new Roo.htmleditor.FilterSpan({ node : d });
30929             new Roo.htmleditor.FilterLongBr({ node : d });
30930             new Roo.htmleditor.FilterComment({ node : d });
30931             
30932             
30933         }
30934         if (this.enableBlocks) {
30935                 
30936             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30937                 if (img.closest('figure')) { // assume!! that it's aready
30938                     return;
30939                 }
30940                 var fig  = new Roo.htmleditor.BlockFigure({
30941                     image_src  : img.src
30942                 });
30943                 fig.updateElement(img); // replace it..
30944                 
30945             });
30946         }
30947         
30948         
30949         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30950         if (this.enableBlocks) {
30951             Roo.htmleditor.Block.initAll(this.doc.body);
30952         }
30953          
30954         
30955         e.preventDefault();
30956         return false;
30957         // default behaveiour should be our local cleanup paste? (optional?)
30958         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30959         //this.owner.fireEvent('paste', e, v);
30960     },
30961     // private
30962     onDestroy : function(){
30963         
30964         
30965         
30966         if(this.rendered){
30967             
30968             //for (var i =0; i < this.toolbars.length;i++) {
30969             //    // fixme - ask toolbars for heights?
30970             //    this.toolbars[i].onDestroy();
30971            // }
30972             
30973             //this.wrap.dom.innerHTML = '';
30974             //this.wrap.remove();
30975         }
30976     },
30977
30978     // private
30979     onFirstFocus : function(){
30980         
30981         this.assignDocWin();
30982         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30983         
30984         this.activated = true;
30985          
30986     
30987         if(Roo.isGecko){ // prevent silly gecko errors
30988             this.win.focus();
30989             var s = this.win.getSelection();
30990             if(!s.focusNode || s.focusNode.nodeType != 3){
30991                 var r = s.getRangeAt(0);
30992                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30993                 r.collapse(true);
30994                 this.deferFocus();
30995             }
30996             try{
30997                 this.execCmd('useCSS', true);
30998                 this.execCmd('styleWithCSS', false);
30999             }catch(e){}
31000         }
31001         this.owner.fireEvent('activate', this);
31002     },
31003
31004     // private
31005     adjustFont: function(btn){
31006         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31007         //if(Roo.isSafari){ // safari
31008         //    adjust *= 2;
31009        // }
31010         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31011         if(Roo.isSafari){ // safari
31012             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31013             v =  (v < 10) ? 10 : v;
31014             v =  (v > 48) ? 48 : v;
31015             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31016             
31017         }
31018         
31019         
31020         v = Math.max(1, v+adjust);
31021         
31022         this.execCmd('FontSize', v  );
31023     },
31024
31025     onEditorEvent : function(e)
31026     {
31027          
31028         
31029         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31030             return; // we do not handle this.. (undo manager does..)
31031         }
31032         // in theory this detects if the last element is not a br, then we try and do that.
31033         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31034         if (e &&
31035             e.target.nodeName == 'BODY' &&
31036             e.type == "mouseup" &&
31037             this.doc.body.lastChild
31038            ) {
31039             var lc = this.doc.body.lastChild;
31040             // gtx-trans is google translate plugin adding crap.
31041             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31042                 lc = lc.previousSibling;
31043             }
31044             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31045             // if last element is <BR> - then dont do anything.
31046             
31047                 var ns = this.doc.createElement('br');
31048                 this.doc.body.appendChild(ns);
31049                 range = this.doc.createRange();
31050                 range.setStartAfter(ns);
31051                 range.collapse(true);
31052                 var sel = this.win.getSelection();
31053                 sel.removeAllRanges();
31054                 sel.addRange(range);
31055             }
31056         }
31057         
31058         
31059         
31060         this.fireEditorEvent(e);
31061       //  this.updateToolbar();
31062         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31063     },
31064     
31065     fireEditorEvent: function(e)
31066     {
31067         this.owner.fireEvent('editorevent', this, e);
31068     },
31069
31070     insertTag : function(tg)
31071     {
31072         // could be a bit smarter... -> wrap the current selected tRoo..
31073         if (tg.toLowerCase() == 'span' ||
31074             tg.toLowerCase() == 'code' ||
31075             tg.toLowerCase() == 'sup' ||
31076             tg.toLowerCase() == 'sub' 
31077             ) {
31078             
31079             range = this.createRange(this.getSelection());
31080             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31081             wrappingNode.appendChild(range.extractContents());
31082             range.insertNode(wrappingNode);
31083
31084             return;
31085             
31086             
31087             
31088         }
31089         this.execCmd("formatblock",   tg);
31090         this.undoManager.addEvent(); 
31091     },
31092     
31093     insertText : function(txt)
31094     {
31095         
31096         
31097         var range = this.createRange();
31098         range.deleteContents();
31099                //alert(Sender.getAttribute('label'));
31100                
31101         range.insertNode(this.doc.createTextNode(txt));
31102         this.undoManager.addEvent();
31103     } ,
31104     
31105      
31106
31107     /**
31108      * Executes a Midas editor command on the editor document and performs necessary focus and
31109      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31110      * @param {String} cmd The Midas command
31111      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31112      */
31113     relayCmd : function(cmd, value)
31114     {
31115         
31116         switch (cmd) {
31117             case 'justifyleft':
31118             case 'justifyright':
31119             case 'justifycenter':
31120                 // if we are in a cell, then we will adjust the
31121                 var n = this.getParentElement();
31122                 var td = n.closest('td');
31123                 if (td) {
31124                     var bl = Roo.htmleditor.Block.factory(td);
31125                     bl.textAlign = cmd.replace('justify','');
31126                     bl.updateElement();
31127                     this.owner.fireEvent('editorevent', this);
31128                     return;
31129                 }
31130                 this.execCmd('styleWithCSS', true); // 
31131                 break;
31132             case 'bold':
31133             case 'italic':
31134                 // if there is no selection, then we insert, and set the curson inside it..
31135                 this.execCmd('styleWithCSS', false); 
31136                 break;
31137                 
31138         
31139             default:
31140                 break;
31141         }
31142         
31143         
31144         this.win.focus();
31145         this.execCmd(cmd, value);
31146         this.owner.fireEvent('editorevent', this);
31147         //this.updateToolbar();
31148         this.owner.deferFocus();
31149     },
31150
31151     /**
31152      * Executes a Midas editor command directly on the editor document.
31153      * For visual commands, you should use {@link #relayCmd} instead.
31154      * <b>This should only be called after the editor is initialized.</b>
31155      * @param {String} cmd The Midas command
31156      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31157      */
31158     execCmd : function(cmd, value){
31159         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31160         this.syncValue();
31161     },
31162  
31163  
31164    
31165     /**
31166      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31167      * to insert tRoo.
31168      * @param {String} text | dom node.. 
31169      */
31170     insertAtCursor : function(text)
31171     {
31172         
31173         if(!this.activated){
31174             return;
31175         }
31176          
31177         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31178             this.win.focus();
31179             
31180             
31181             // from jquery ui (MIT licenced)
31182             var range, node;
31183             var win = this.win;
31184             
31185             if (win.getSelection && win.getSelection().getRangeAt) {
31186                 
31187                 // delete the existing?
31188                 
31189                 this.createRange(this.getSelection()).deleteContents();
31190                 range = win.getSelection().getRangeAt(0);
31191                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31192                 range.insertNode(node);
31193                 range = range.cloneRange();
31194                 range.collapse(false);
31195                  
31196                 win.getSelection().removeAllRanges();
31197                 win.getSelection().addRange(range);
31198                 
31199                 
31200                 
31201             } else if (win.document.selection && win.document.selection.createRange) {
31202                 // no firefox support
31203                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31204                 win.document.selection.createRange().pasteHTML(txt);
31205             
31206             } else {
31207                 // no firefox support
31208                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31209                 this.execCmd('InsertHTML', txt);
31210             } 
31211             this.syncValue();
31212             
31213             this.deferFocus();
31214         }
31215     },
31216  // private
31217     mozKeyPress : function(e){
31218         if(e.ctrlKey){
31219             var c = e.getCharCode(), cmd;
31220           
31221             if(c > 0){
31222                 c = String.fromCharCode(c).toLowerCase();
31223                 switch(c){
31224                     case 'b':
31225                         cmd = 'bold';
31226                         break;
31227                     case 'i':
31228                         cmd = 'italic';
31229                         break;
31230                     
31231                     case 'u':
31232                         cmd = 'underline';
31233                         break;
31234                     
31235                     //case 'v':
31236                       //  this.cleanUpPaste.defer(100, this);
31237                       //  return;
31238                         
31239                 }
31240                 if(cmd){
31241                     
31242                     this.relayCmd(cmd);
31243                     //this.win.focus();
31244                     //this.execCmd(cmd);
31245                     //this.deferFocus();
31246                     e.preventDefault();
31247                 }
31248                 
31249             }
31250         }
31251     },
31252
31253     // private
31254     fixKeys : function(){ // load time branching for fastest keydown performance
31255         
31256         
31257         if(Roo.isIE){
31258             return function(e){
31259                 var k = e.getKey(), r;
31260                 if(k == e.TAB){
31261                     e.stopEvent();
31262                     r = this.doc.selection.createRange();
31263                     if(r){
31264                         r.collapse(true);
31265                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31266                         this.deferFocus();
31267                     }
31268                     return;
31269                 }
31270                 /// this is handled by Roo.htmleditor.KeyEnter
31271                  /*
31272                 if(k == e.ENTER){
31273                     r = this.doc.selection.createRange();
31274                     if(r){
31275                         var target = r.parentElement();
31276                         if(!target || target.tagName.toLowerCase() != 'li'){
31277                             e.stopEvent();
31278                             r.pasteHTML('<br/>');
31279                             r.collapse(false);
31280                             r.select();
31281                         }
31282                     }
31283                 }
31284                 */
31285                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31286                 //    this.cleanUpPaste.defer(100, this);
31287                 //    return;
31288                 //}
31289                 
31290                 
31291             };
31292         }else if(Roo.isOpera){
31293             return function(e){
31294                 var k = e.getKey();
31295                 if(k == e.TAB){
31296                     e.stopEvent();
31297                     this.win.focus();
31298                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31299                     this.deferFocus();
31300                 }
31301                
31302                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31303                 //    this.cleanUpPaste.defer(100, this);
31304                  //   return;
31305                 //}
31306                 
31307             };
31308         }else if(Roo.isSafari){
31309             return function(e){
31310                 var k = e.getKey();
31311                 
31312                 if(k == e.TAB){
31313                     e.stopEvent();
31314                     this.execCmd('InsertText','\t');
31315                     this.deferFocus();
31316                     return;
31317                 }
31318                  this.mozKeyPress(e);
31319                 
31320                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31321                  //   this.cleanUpPaste.defer(100, this);
31322                  //   return;
31323                // }
31324                 
31325              };
31326         }
31327     }(),
31328     
31329     getAllAncestors: function()
31330     {
31331         var p = this.getSelectedNode();
31332         var a = [];
31333         if (!p) {
31334             a.push(p); // push blank onto stack..
31335             p = this.getParentElement();
31336         }
31337         
31338         
31339         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31340             a.push(p);
31341             p = p.parentNode;
31342         }
31343         a.push(this.doc.body);
31344         return a;
31345     },
31346     lastSel : false,
31347     lastSelNode : false,
31348     
31349     
31350     getSelection : function() 
31351     {
31352         this.assignDocWin();
31353         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31354     },
31355     /**
31356      * Select a dom node
31357      * @param {DomElement} node the node to select
31358      */
31359     selectNode : function(node, collapse)
31360     {
31361         var nodeRange = node.ownerDocument.createRange();
31362         try {
31363             nodeRange.selectNode(node);
31364         } catch (e) {
31365             nodeRange.selectNodeContents(node);
31366         }
31367         if (collapse === true) {
31368             nodeRange.collapse(true);
31369         }
31370         //
31371         var s = this.win.getSelection();
31372         s.removeAllRanges();
31373         s.addRange(nodeRange);
31374     },
31375     
31376     getSelectedNode: function() 
31377     {
31378         // this may only work on Gecko!!!
31379         
31380         // should we cache this!!!!
31381         
31382          
31383          
31384         var range = this.createRange(this.getSelection()).cloneRange();
31385         
31386         if (Roo.isIE) {
31387             var parent = range.parentElement();
31388             while (true) {
31389                 var testRange = range.duplicate();
31390                 testRange.moveToElementText(parent);
31391                 if (testRange.inRange(range)) {
31392                     break;
31393                 }
31394                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31395                     break;
31396                 }
31397                 parent = parent.parentElement;
31398             }
31399             return parent;
31400         }
31401         
31402         // is ancestor a text element.
31403         var ac =  range.commonAncestorContainer;
31404         if (ac.nodeType == 3) {
31405             ac = ac.parentNode;
31406         }
31407         
31408         var ar = ac.childNodes;
31409          
31410         var nodes = [];
31411         var other_nodes = [];
31412         var has_other_nodes = false;
31413         for (var i=0;i<ar.length;i++) {
31414             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31415                 continue;
31416             }
31417             // fullly contained node.
31418             
31419             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31420                 nodes.push(ar[i]);
31421                 continue;
31422             }
31423             
31424             // probably selected..
31425             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31426                 other_nodes.push(ar[i]);
31427                 continue;
31428             }
31429             // outer..
31430             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31431                 continue;
31432             }
31433             
31434             
31435             has_other_nodes = true;
31436         }
31437         if (!nodes.length && other_nodes.length) {
31438             nodes= other_nodes;
31439         }
31440         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31441             return false;
31442         }
31443         
31444         return nodes[0];
31445     },
31446     
31447     
31448     createRange: function(sel)
31449     {
31450         // this has strange effects when using with 
31451         // top toolbar - not sure if it's a great idea.
31452         //this.editor.contentWindow.focus();
31453         if (typeof sel != "undefined") {
31454             try {
31455                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31456             } catch(e) {
31457                 return this.doc.createRange();
31458             }
31459         } else {
31460             return this.doc.createRange();
31461         }
31462     },
31463     getParentElement: function()
31464     {
31465         
31466         this.assignDocWin();
31467         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31468         
31469         var range = this.createRange(sel);
31470          
31471         try {
31472             var p = range.commonAncestorContainer;
31473             while (p.nodeType == 3) { // text node
31474                 p = p.parentNode;
31475             }
31476             return p;
31477         } catch (e) {
31478             return null;
31479         }
31480     
31481     },
31482     /***
31483      *
31484      * Range intersection.. the hard stuff...
31485      *  '-1' = before
31486      *  '0' = hits..
31487      *  '1' = after.
31488      *         [ -- selected range --- ]
31489      *   [fail]                        [fail]
31490      *
31491      *    basically..
31492      *      if end is before start or  hits it. fail.
31493      *      if start is after end or hits it fail.
31494      *
31495      *   if either hits (but other is outside. - then it's not 
31496      *   
31497      *    
31498      **/
31499     
31500     
31501     // @see http://www.thismuchiknow.co.uk/?p=64.
31502     rangeIntersectsNode : function(range, node)
31503     {
31504         var nodeRange = node.ownerDocument.createRange();
31505         try {
31506             nodeRange.selectNode(node);
31507         } catch (e) {
31508             nodeRange.selectNodeContents(node);
31509         }
31510     
31511         var rangeStartRange = range.cloneRange();
31512         rangeStartRange.collapse(true);
31513     
31514         var rangeEndRange = range.cloneRange();
31515         rangeEndRange.collapse(false);
31516     
31517         var nodeStartRange = nodeRange.cloneRange();
31518         nodeStartRange.collapse(true);
31519     
31520         var nodeEndRange = nodeRange.cloneRange();
31521         nodeEndRange.collapse(false);
31522     
31523         return rangeStartRange.compareBoundaryPoints(
31524                  Range.START_TO_START, nodeEndRange) == -1 &&
31525                rangeEndRange.compareBoundaryPoints(
31526                  Range.START_TO_START, nodeStartRange) == 1;
31527         
31528          
31529     },
31530     rangeCompareNode : function(range, node)
31531     {
31532         var nodeRange = node.ownerDocument.createRange();
31533         try {
31534             nodeRange.selectNode(node);
31535         } catch (e) {
31536             nodeRange.selectNodeContents(node);
31537         }
31538         
31539         
31540         range.collapse(true);
31541     
31542         nodeRange.collapse(true);
31543      
31544         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31545         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31546          
31547         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31548         
31549         var nodeIsBefore   =  ss == 1;
31550         var nodeIsAfter    = ee == -1;
31551         
31552         if (nodeIsBefore && nodeIsAfter) {
31553             return 0; // outer
31554         }
31555         if (!nodeIsBefore && nodeIsAfter) {
31556             return 1; //right trailed.
31557         }
31558         
31559         if (nodeIsBefore && !nodeIsAfter) {
31560             return 2;  // left trailed.
31561         }
31562         // fully contined.
31563         return 3;
31564     },
31565  
31566     cleanWordChars : function(input) {// change the chars to hex code
31567         
31568        var swapCodes  = [ 
31569             [    8211, "&#8211;" ], 
31570             [    8212, "&#8212;" ], 
31571             [    8216,  "'" ],  
31572             [    8217, "'" ],  
31573             [    8220, '"' ],  
31574             [    8221, '"' ],  
31575             [    8226, "*" ],  
31576             [    8230, "..." ]
31577         ]; 
31578         var output = input;
31579         Roo.each(swapCodes, function(sw) { 
31580             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31581             
31582             output = output.replace(swapper, sw[1]);
31583         });
31584         
31585         return output;
31586     },
31587     
31588      
31589     
31590         
31591     
31592     cleanUpChild : function (node)
31593     {
31594         
31595         new Roo.htmleditor.FilterComment({node : node});
31596         new Roo.htmleditor.FilterAttributes({
31597                 node : node,
31598                 attrib_black : this.ablack,
31599                 attrib_clean : this.aclean,
31600                 style_white : this.cwhite,
31601                 style_black : this.cblack
31602         });
31603         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31604         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31605          
31606         
31607     },
31608     
31609     /**
31610      * Clean up MS wordisms...
31611      * @deprecated - use filter directly
31612      */
31613     cleanWord : function(node)
31614     {
31615         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31616         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31617         
31618     },
31619    
31620     
31621     /**
31622
31623      * @deprecated - use filters
31624      */
31625     cleanTableWidths : function(node)
31626     {
31627         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31628         
31629  
31630     },
31631     
31632      
31633         
31634     applyBlacklists : function()
31635     {
31636         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31637         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31638         
31639         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31640         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31641         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31642         
31643         this.white = [];
31644         this.black = [];
31645         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31646             if (b.indexOf(tag) > -1) {
31647                 return;
31648             }
31649             this.white.push(tag);
31650             
31651         }, this);
31652         
31653         Roo.each(w, function(tag) {
31654             if (b.indexOf(tag) > -1) {
31655                 return;
31656             }
31657             if (this.white.indexOf(tag) > -1) {
31658                 return;
31659             }
31660             this.white.push(tag);
31661             
31662         }, this);
31663         
31664         
31665         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31666             if (w.indexOf(tag) > -1) {
31667                 return;
31668             }
31669             this.black.push(tag);
31670             
31671         }, this);
31672         
31673         Roo.each(b, function(tag) {
31674             if (w.indexOf(tag) > -1) {
31675                 return;
31676             }
31677             if (this.black.indexOf(tag) > -1) {
31678                 return;
31679             }
31680             this.black.push(tag);
31681             
31682         }, this);
31683         
31684         
31685         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31686         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31687         
31688         this.cwhite = [];
31689         this.cblack = [];
31690         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31691             if (b.indexOf(tag) > -1) {
31692                 return;
31693             }
31694             this.cwhite.push(tag);
31695             
31696         }, this);
31697         
31698         Roo.each(w, function(tag) {
31699             if (b.indexOf(tag) > -1) {
31700                 return;
31701             }
31702             if (this.cwhite.indexOf(tag) > -1) {
31703                 return;
31704             }
31705             this.cwhite.push(tag);
31706             
31707         }, this);
31708         
31709         
31710         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31711             if (w.indexOf(tag) > -1) {
31712                 return;
31713             }
31714             this.cblack.push(tag);
31715             
31716         }, this);
31717         
31718         Roo.each(b, function(tag) {
31719             if (w.indexOf(tag) > -1) {
31720                 return;
31721             }
31722             if (this.cblack.indexOf(tag) > -1) {
31723                 return;
31724             }
31725             this.cblack.push(tag);
31726             
31727         }, this);
31728     },
31729     
31730     setStylesheets : function(stylesheets)
31731     {
31732         if(typeof(stylesheets) == 'string'){
31733             Roo.get(this.iframe.contentDocument.head).createChild({
31734                 tag : 'link',
31735                 rel : 'stylesheet',
31736                 type : 'text/css',
31737                 href : stylesheets
31738             });
31739             
31740             return;
31741         }
31742         var _this = this;
31743      
31744         Roo.each(stylesheets, function(s) {
31745             if(!s.length){
31746                 return;
31747             }
31748             
31749             Roo.get(_this.iframe.contentDocument.head).createChild({
31750                 tag : 'link',
31751                 rel : 'stylesheet',
31752                 type : 'text/css',
31753                 href : s
31754             });
31755         });
31756
31757         
31758     },
31759     
31760     
31761     updateLanguage : function()
31762     {
31763         if (!this.iframe || !this.iframe.contentDocument) {
31764             return;
31765         }
31766         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31767     },
31768     
31769     
31770     removeStylesheets : function()
31771     {
31772         var _this = this;
31773         
31774         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31775             s.remove();
31776         });
31777     },
31778     
31779     setStyle : function(style)
31780     {
31781         Roo.get(this.iframe.contentDocument.head).createChild({
31782             tag : 'style',
31783             type : 'text/css',
31784             html : style
31785         });
31786
31787         return;
31788     }
31789     
31790     // hide stuff that is not compatible
31791     /**
31792      * @event blur
31793      * @hide
31794      */
31795     /**
31796      * @event change
31797      * @hide
31798      */
31799     /**
31800      * @event focus
31801      * @hide
31802      */
31803     /**
31804      * @event specialkey
31805      * @hide
31806      */
31807     /**
31808      * @cfg {String} fieldClass @hide
31809      */
31810     /**
31811      * @cfg {String} focusClass @hide
31812      */
31813     /**
31814      * @cfg {String} autoCreate @hide
31815      */
31816     /**
31817      * @cfg {String} inputType @hide
31818      */
31819     /**
31820      * @cfg {String} invalidClass @hide
31821      */
31822     /**
31823      * @cfg {String} invalidText @hide
31824      */
31825     /**
31826      * @cfg {String} msgFx @hide
31827      */
31828     /**
31829      * @cfg {String} validateOnBlur @hide
31830      */
31831 });
31832
31833 Roo.HtmlEditorCore.white = [
31834         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31835         
31836        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31837        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31838        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31839        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31840        'TABLE',   'UL',         'XMP', 
31841        
31842        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31843       'THEAD',   'TR', 
31844      
31845       'DIR', 'MENU', 'OL', 'UL', 'DL',
31846        
31847       'EMBED',  'OBJECT'
31848 ];
31849
31850
31851 Roo.HtmlEditorCore.black = [
31852     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31853         'APPLET', // 
31854         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31855         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31856         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31857         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31858         //'FONT' // CLEAN LATER..
31859         'COLGROUP', 'COL'   // messy tables.
31860         
31861         
31862 ];
31863 Roo.HtmlEditorCore.clean = [ // ?? needed???
31864      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31865 ];
31866 Roo.HtmlEditorCore.tag_remove = [
31867     'FONT', 'TBODY'  
31868 ];
31869 // attributes..
31870
31871 Roo.HtmlEditorCore.ablack = [
31872     'on'
31873 ];
31874     
31875 Roo.HtmlEditorCore.aclean = [ 
31876     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31877 ];
31878
31879 // protocols..
31880 Roo.HtmlEditorCore.pwhite= [
31881         'http',  'https',  'mailto'
31882 ];
31883
31884 // white listed style attributes.
31885 Roo.HtmlEditorCore.cwhite= [
31886       //  'text-align', /// default is to allow most things..
31887       
31888          
31889 //        'font-size'//??
31890 ];
31891
31892 // black listed style attributes.
31893 Roo.HtmlEditorCore.cblack= [
31894       //  'font-size' -- this can be set by the project 
31895 ];
31896
31897
31898
31899
31900     /*
31901  * - LGPL
31902  *
31903  * HtmlEditor
31904  * 
31905  */
31906
31907 /**
31908  * @class Roo.bootstrap.form.HtmlEditor
31909  * @extends Roo.bootstrap.form.TextArea
31910  * Bootstrap HtmlEditor class
31911
31912  * @constructor
31913  * Create a new HtmlEditor
31914  * @param {Object} config The config object
31915  */
31916
31917 Roo.bootstrap.form.HtmlEditor = function(config){
31918     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31919     if (!this.toolbars) {
31920         this.toolbars = [];
31921     }
31922     
31923     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31924     this.addEvents({
31925             /**
31926              * @event initialize
31927              * Fires when the editor is fully initialized (including the iframe)
31928              * @param {HtmlEditor} this
31929              */
31930             initialize: true,
31931             /**
31932              * @event activate
31933              * Fires when the editor is first receives the focus. Any insertion must wait
31934              * until after this event.
31935              * @param {HtmlEditor} this
31936              */
31937             activate: true,
31938              /**
31939              * @event beforesync
31940              * Fires before the textarea is updated with content from the editor iframe. Return false
31941              * to cancel the sync.
31942              * @param {HtmlEditor} this
31943              * @param {String} html
31944              */
31945             beforesync: true,
31946              /**
31947              * @event beforepush
31948              * Fires before the iframe editor is updated with content from the textarea. Return false
31949              * to cancel the push.
31950              * @param {HtmlEditor} this
31951              * @param {String} html
31952              */
31953             beforepush: true,
31954              /**
31955              * @event sync
31956              * Fires when the textarea is updated with content from the editor iframe.
31957              * @param {HtmlEditor} this
31958              * @param {String} html
31959              */
31960             sync: true,
31961              /**
31962              * @event push
31963              * Fires when the iframe editor is updated with content from the textarea.
31964              * @param {HtmlEditor} this
31965              * @param {String} html
31966              */
31967             push: true,
31968              /**
31969              * @event editmodechange
31970              * Fires when the editor switches edit modes
31971              * @param {HtmlEditor} this
31972              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31973              */
31974             editmodechange: true,
31975             /**
31976              * @event editorevent
31977              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31978              * @param {HtmlEditor} this
31979              */
31980             editorevent: true,
31981             /**
31982              * @event firstfocus
31983              * Fires when on first focus - needed by toolbars..
31984              * @param {HtmlEditor} this
31985              */
31986             firstfocus: true,
31987             /**
31988              * @event autosave
31989              * Auto save the htmlEditor value as a file into Events
31990              * @param {HtmlEditor} this
31991              */
31992             autosave: true,
31993             /**
31994              * @event savedpreview
31995              * preview the saved version of htmlEditor
31996              * @param {HtmlEditor} this
31997              */
31998             savedpreview: true
31999         });
32000 };
32001
32002
32003 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32004     
32005     
32006       /**
32007      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32008      */
32009     toolbars : false,
32010     
32011      /**
32012     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32013     */
32014     btns : [],
32015    
32016      /**
32017      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32018      *                        Roo.resizable.
32019      */
32020     resizable : false,
32021      /**
32022      * @cfg {Number} height (in pixels)
32023      */   
32024     height: 300,
32025    /**
32026      * @cfg {Number} width (in pixels)
32027      */   
32028     width: false,
32029     
32030     /**
32031      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32032      * 
32033      */
32034     stylesheets: false,
32035     
32036     // id of frame..
32037     frameId: false,
32038     
32039     // private properties
32040     validationEvent : false,
32041     deferHeight: true,
32042     initialized : false,
32043     activated : false,
32044     
32045     onFocus : Roo.emptyFn,
32046     iframePad:3,
32047     hideMode:'offsets',
32048     
32049     tbContainer : false,
32050     
32051     bodyCls : '',
32052     
32053     toolbarContainer :function() {
32054         return this.wrap.select('.x-html-editor-tb',true).first();
32055     },
32056
32057     /**
32058      * Protected method that will not generally be called directly. It
32059      * is called when the editor creates its toolbar. Override this method if you need to
32060      * add custom toolbar buttons.
32061      * @param {HtmlEditor} editor
32062      */
32063     createToolbar : function(){
32064         Roo.log('renewing');
32065         Roo.log("create toolbars");
32066         
32067         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32068         this.toolbars[0].render(this.toolbarContainer());
32069         
32070         return;
32071         
32072 //        if (!editor.toolbars || !editor.toolbars.length) {
32073 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32074 //        }
32075 //        
32076 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32077 //            editor.toolbars[i] = Roo.factory(
32078 //                    typeof(editor.toolbars[i]) == 'string' ?
32079 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32080 //                Roo.bootstrap.form.HtmlEditor);
32081 //            editor.toolbars[i].init(editor);
32082 //        }
32083     },
32084
32085      
32086     // private
32087     onRender : function(ct, position)
32088     {
32089        // Roo.log("Call onRender: " + this.xtype);
32090         var _t = this;
32091         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32092       
32093         this.wrap = this.inputEl().wrap({
32094             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32095         });
32096         
32097         this.editorcore.onRender(ct, position);
32098          
32099         if (this.resizable) {
32100             this.resizeEl = new Roo.Resizable(this.wrap, {
32101                 pinned : true,
32102                 wrap: true,
32103                 dynamic : true,
32104                 minHeight : this.height,
32105                 height: this.height,
32106                 handles : this.resizable,
32107                 width: this.width,
32108                 listeners : {
32109                     resize : function(r, w, h) {
32110                         _t.onResize(w,h); // -something
32111                     }
32112                 }
32113             });
32114             
32115         }
32116         this.createToolbar(this);
32117        
32118         
32119         if(!this.width && this.resizable){
32120             this.setSize(this.wrap.getSize());
32121         }
32122         if (this.resizeEl) {
32123             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32124             // should trigger onReize..
32125         }
32126         
32127     },
32128
32129     // private
32130     onResize : function(w, h)
32131     {
32132         Roo.log('resize: ' +w + ',' + h );
32133         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32134         var ew = false;
32135         var eh = false;
32136         
32137         if(this.inputEl() ){
32138             if(typeof w == 'number'){
32139                 var aw = w - this.wrap.getFrameWidth('lr');
32140                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32141                 ew = aw;
32142             }
32143             if(typeof h == 'number'){
32144                  var tbh = -11;  // fixme it needs to tool bar size!
32145                 for (var i =0; i < this.toolbars.length;i++) {
32146                     // fixme - ask toolbars for heights?
32147                     tbh += this.toolbars[i].el.getHeight();
32148                     //if (this.toolbars[i].footer) {
32149                     //    tbh += this.toolbars[i].footer.el.getHeight();
32150                     //}
32151                 }
32152               
32153                 
32154                 
32155                 
32156                 
32157                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32158                 ah -= 5; // knock a few pixes off for look..
32159                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32160                 var eh = ah;
32161             }
32162         }
32163         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32164         this.editorcore.onResize(ew,eh);
32165         
32166     },
32167
32168     /**
32169      * Toggles the editor between standard and source edit mode.
32170      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32171      */
32172     toggleSourceEdit : function(sourceEditMode)
32173     {
32174         this.editorcore.toggleSourceEdit(sourceEditMode);
32175         
32176         if(this.editorcore.sourceEditMode){
32177             Roo.log('editor - showing textarea');
32178             
32179 //            Roo.log('in');
32180 //            Roo.log(this.syncValue());
32181             this.syncValue();
32182             this.inputEl().removeClass(['hide', 'x-hidden']);
32183             this.inputEl().dom.removeAttribute('tabIndex');
32184             this.inputEl().focus();
32185         }else{
32186             Roo.log('editor - hiding textarea');
32187 //            Roo.log('out')
32188 //            Roo.log(this.pushValue()); 
32189             this.pushValue();
32190             
32191             this.inputEl().addClass(['hide', 'x-hidden']);
32192             this.inputEl().dom.setAttribute('tabIndex', -1);
32193             //this.deferFocus();
32194         }
32195          
32196         if(this.resizable){
32197             this.setSize(this.wrap.getSize());
32198         }
32199         
32200         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32201     },
32202  
32203     // private (for BoxComponent)
32204     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32205
32206     // private (for BoxComponent)
32207     getResizeEl : function(){
32208         return this.wrap;
32209     },
32210
32211     // private (for BoxComponent)
32212     getPositionEl : function(){
32213         return this.wrap;
32214     },
32215
32216     // private
32217     initEvents : function(){
32218         this.originalValue = this.getValue();
32219     },
32220
32221 //    /**
32222 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32223 //     * @method
32224 //     */
32225 //    markInvalid : Roo.emptyFn,
32226 //    /**
32227 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32228 //     * @method
32229 //     */
32230 //    clearInvalid : Roo.emptyFn,
32231
32232     setValue : function(v){
32233         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32234         this.editorcore.pushValue();
32235     },
32236
32237      
32238     // private
32239     deferFocus : function(){
32240         this.focus.defer(10, this);
32241     },
32242
32243     // doc'ed in Field
32244     focus : function(){
32245         this.editorcore.focus();
32246         
32247     },
32248       
32249
32250     // private
32251     onDestroy : function(){
32252         
32253         
32254         
32255         if(this.rendered){
32256             
32257             for (var i =0; i < this.toolbars.length;i++) {
32258                 // fixme - ask toolbars for heights?
32259                 this.toolbars[i].onDestroy();
32260             }
32261             
32262             this.wrap.dom.innerHTML = '';
32263             this.wrap.remove();
32264         }
32265     },
32266
32267     // private
32268     onFirstFocus : function(){
32269         //Roo.log("onFirstFocus");
32270         this.editorcore.onFirstFocus();
32271          for (var i =0; i < this.toolbars.length;i++) {
32272             this.toolbars[i].onFirstFocus();
32273         }
32274         
32275     },
32276     
32277     // private
32278     syncValue : function()
32279     {   
32280         this.editorcore.syncValue();
32281     },
32282     
32283     pushValue : function()
32284     {   
32285         this.editorcore.pushValue();
32286     }
32287      
32288     
32289     // hide stuff that is not compatible
32290     /**
32291      * @event blur
32292      * @hide
32293      */
32294     /**
32295      * @event change
32296      * @hide
32297      */
32298     /**
32299      * @event focus
32300      * @hide
32301      */
32302     /**
32303      * @event specialkey
32304      * @hide
32305      */
32306     /**
32307      * @cfg {String} fieldClass @hide
32308      */
32309     /**
32310      * @cfg {String} focusClass @hide
32311      */
32312     /**
32313      * @cfg {String} autoCreate @hide
32314      */
32315     /**
32316      * @cfg {String} inputType @hide
32317      */
32318      
32319     /**
32320      * @cfg {String} invalidText @hide
32321      */
32322     /**
32323      * @cfg {String} msgFx @hide
32324      */
32325     /**
32326      * @cfg {String} validateOnBlur @hide
32327      */
32328 });
32329  
32330     
32331    
32332    
32333    
32334       
32335 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32336 /**
32337  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32338  * @parent Roo.bootstrap.form.HtmlEditor
32339  * @extends Roo.bootstrap.nav.Simplebar
32340  * Basic Toolbar
32341  * 
32342  * @example
32343  * Usage:
32344  *
32345  new Roo.bootstrap.form.HtmlEditor({
32346     ....
32347     toolbars : [
32348         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32349             disable : { fonts: 1 , format: 1, ..., ... , ...],
32350             btns : [ .... ]
32351         })
32352     }
32353      
32354  * 
32355  * @cfg {Object} disable List of elements to disable..
32356  * @cfg {Array} btns List of additional buttons.
32357  * 
32358  * 
32359  * NEEDS Extra CSS? 
32360  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32361  */
32362  
32363 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32364 {
32365     
32366     Roo.apply(this, config);
32367     
32368     // default disabled, based on 'good practice'..
32369     this.disable = this.disable || {};
32370     Roo.applyIf(this.disable, {
32371         fontSize : true,
32372         colors : true,
32373         specialElements : true
32374     });
32375     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32376     
32377     this.editor = config.editor;
32378     this.editorcore = config.editor.editorcore;
32379     
32380     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32381     
32382     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32383     // dont call parent... till later.
32384 }
32385 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32386      
32387     bar : true,
32388     
32389     editor : false,
32390     editorcore : false,
32391     
32392     
32393     formats : [
32394         "p" ,  
32395         "h1","h2","h3","h4","h5","h6", 
32396         "pre", "code", 
32397         "abbr", "acronym", "address", "cite", "samp", "var",
32398         'div','span'
32399     ],
32400     
32401     onRender : function(ct, position)
32402     {
32403        // Roo.log("Call onRender: " + this.xtype);
32404         
32405        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32406        Roo.log(this.el);
32407        this.el.dom.style.marginBottom = '0';
32408        var _this = this;
32409        var editorcore = this.editorcore;
32410        var editor= this.editor;
32411        
32412        var children = [];
32413        var btn = function(id,cmd , toggle, handler, html){
32414        
32415             var  event = toggle ? 'toggle' : 'click';
32416        
32417             var a = {
32418                 size : 'sm',
32419                 xtype: 'Button',
32420                 xns: Roo.bootstrap,
32421                 //glyphicon : id,
32422                 fa: id,
32423                 cmd : id || cmd,
32424                 enableToggle:toggle !== false,
32425                 html : html || '',
32426                 pressed : toggle ? false : null,
32427                 listeners : {}
32428             };
32429             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32430                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32431             };
32432             children.push(a);
32433             return a;
32434        }
32435        
32436     //    var cb_box = function...
32437         
32438         var style = {
32439                 xtype: 'Button',
32440                 size : 'sm',
32441                 xns: Roo.bootstrap,
32442                 fa : 'font',
32443                 //html : 'submit'
32444                 menu : {
32445                     xtype: 'Menu',
32446                     xns: Roo.bootstrap,
32447                     items:  []
32448                 }
32449         };
32450         Roo.each(this.formats, function(f) {
32451             style.menu.items.push({
32452                 xtype :'MenuItem',
32453                 xns: Roo.bootstrap,
32454                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32455                 tagname : f,
32456                 listeners : {
32457                     click : function()
32458                     {
32459                         editorcore.insertTag(this.tagname);
32460                         editor.focus();
32461                     }
32462                 }
32463                 
32464             });
32465         });
32466         children.push(style);   
32467         
32468         btn('bold',false,true);
32469         btn('italic',false,true);
32470         btn('align-left', 'justifyleft',true);
32471         btn('align-center', 'justifycenter',true);
32472         btn('align-right' , 'justifyright',true);
32473         btn('link', false, false, function(btn) {
32474             //Roo.log("create link?");
32475             var url = prompt(this.createLinkText, this.defaultLinkValue);
32476             if(url && url != 'http:/'+'/'){
32477                 this.editorcore.relayCmd('createlink', url);
32478             }
32479         }),
32480         btn('list','insertunorderedlist',true);
32481         btn('pencil', false,true, function(btn){
32482                 Roo.log(this);
32483                 this.toggleSourceEdit(btn.pressed);
32484         });
32485         
32486         if (this.editor.btns.length > 0) {
32487             for (var i = 0; i<this.editor.btns.length; i++) {
32488                 children.push(this.editor.btns[i]);
32489             }
32490         }
32491         
32492         /*
32493         var cog = {
32494                 xtype: 'Button',
32495                 size : 'sm',
32496                 xns: Roo.bootstrap,
32497                 glyphicon : 'cog',
32498                 //html : 'submit'
32499                 menu : {
32500                     xtype: 'Menu',
32501                     xns: Roo.bootstrap,
32502                     items:  []
32503                 }
32504         };
32505         
32506         cog.menu.items.push({
32507             xtype :'MenuItem',
32508             xns: Roo.bootstrap,
32509             html : Clean styles,
32510             tagname : f,
32511             listeners : {
32512                 click : function()
32513                 {
32514                     editorcore.insertTag(this.tagname);
32515                     editor.focus();
32516                 }
32517             }
32518             
32519         });
32520        */
32521         
32522          
32523        this.xtype = 'NavSimplebar';
32524         
32525         for(var i=0;i< children.length;i++) {
32526             
32527             this.buttons.add(this.addxtypeChild(children[i]));
32528             
32529         }
32530         
32531         editor.on('editorevent', this.updateToolbar, this);
32532     },
32533     onBtnClick : function(id)
32534     {
32535        this.editorcore.relayCmd(id);
32536        this.editorcore.focus();
32537     },
32538     
32539     /**
32540      * Protected method that will not generally be called directly. It triggers
32541      * a toolbar update by reading the markup state of the current selection in the editor.
32542      */
32543     updateToolbar: function(){
32544
32545         if(!this.editorcore.activated){
32546             this.editor.onFirstFocus(); // is this neeed?
32547             return;
32548         }
32549
32550         var btns = this.buttons; 
32551         var doc = this.editorcore.doc;
32552         btns.get('bold').setActive(doc.queryCommandState('bold'));
32553         btns.get('italic').setActive(doc.queryCommandState('italic'));
32554         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32555         
32556         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32557         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32558         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32559         
32560         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32561         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32562          /*
32563         
32564         var ans = this.editorcore.getAllAncestors();
32565         if (this.formatCombo) {
32566             
32567             
32568             var store = this.formatCombo.store;
32569             this.formatCombo.setValue("");
32570             for (var i =0; i < ans.length;i++) {
32571                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32572                     // select it..
32573                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32574                     break;
32575                 }
32576             }
32577         }
32578         
32579         
32580         
32581         // hides menus... - so this cant be on a menu...
32582         Roo.bootstrap.MenuMgr.hideAll();
32583         */
32584         Roo.bootstrap.menu.Manager.hideAll();
32585         //this.editorsyncValue();
32586     },
32587     onFirstFocus: function() {
32588         this.buttons.each(function(item){
32589            item.enable();
32590         });
32591     },
32592     toggleSourceEdit : function(sourceEditMode){
32593         
32594           
32595         if(sourceEditMode){
32596             Roo.log("disabling buttons");
32597            this.buttons.each( function(item){
32598                 if(item.cmd != 'pencil'){
32599                     item.disable();
32600                 }
32601             });
32602           
32603         }else{
32604             Roo.log("enabling buttons");
32605             if(this.editorcore.initialized){
32606                 this.buttons.each( function(item){
32607                     item.enable();
32608                 });
32609             }
32610             
32611         }
32612         Roo.log("calling toggole on editor");
32613         // tell the editor that it's been pressed..
32614         this.editor.toggleSourceEdit(sourceEditMode);
32615        
32616     }
32617 });
32618
32619
32620
32621
32622  
32623 /*
32624  * - LGPL
32625  */
32626
32627 /**
32628  * @class Roo.bootstrap.form.Markdown
32629  * @extends Roo.bootstrap.form.TextArea
32630  * Bootstrap Showdown editable area
32631  * @cfg {string} content
32632  * 
32633  * @constructor
32634  * Create a new Showdown
32635  */
32636
32637 Roo.bootstrap.form.Markdown = function(config){
32638     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32639    
32640 };
32641
32642 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32643     
32644     editing :false,
32645     
32646     initEvents : function()
32647     {
32648         
32649         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32650         this.markdownEl = this.el.createChild({
32651             cls : 'roo-markdown-area'
32652         });
32653         this.inputEl().addClass('d-none');
32654         if (this.getValue() == '') {
32655             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32656             
32657         } else {
32658             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32659         }
32660         this.markdownEl.on('click', this.toggleTextEdit, this);
32661         this.on('blur', this.toggleTextEdit, this);
32662         this.on('specialkey', this.resizeTextArea, this);
32663     },
32664     
32665     toggleTextEdit : function()
32666     {
32667         var sh = this.markdownEl.getHeight();
32668         this.inputEl().addClass('d-none');
32669         this.markdownEl.addClass('d-none');
32670         if (!this.editing) {
32671             // show editor?
32672             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32673             this.inputEl().removeClass('d-none');
32674             this.inputEl().focus();
32675             this.editing = true;
32676             return;
32677         }
32678         // show showdown...
32679         this.updateMarkdown();
32680         this.markdownEl.removeClass('d-none');
32681         this.editing = false;
32682         return;
32683     },
32684     updateMarkdown : function()
32685     {
32686         if (this.getValue() == '') {
32687             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32688             return;
32689         }
32690  
32691         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32692     },
32693     
32694     resizeTextArea: function () {
32695         
32696         var sh = 100;
32697         Roo.log([sh, this.getValue().split("\n").length * 30]);
32698         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32699     },
32700     setValue : function(val)
32701     {
32702         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32703         if (!this.editing) {
32704             this.updateMarkdown();
32705         }
32706         
32707     },
32708     focus : function()
32709     {
32710         if (!this.editing) {
32711             this.toggleTextEdit();
32712         }
32713         
32714     }
32715
32716
32717 });/*
32718  * Based on:
32719  * Ext JS Library 1.1.1
32720  * Copyright(c) 2006-2007, Ext JS, LLC.
32721  *
32722  * Originally Released Under LGPL - original licence link has changed is not relivant.
32723  *
32724  * Fork - LGPL
32725  * <script type="text/javascript">
32726  */
32727  
32728 /**
32729  * @class Roo.bootstrap.PagingToolbar
32730  * @extends Roo.bootstrap.nav.Simplebar
32731  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32732  * @constructor
32733  * Create a new PagingToolbar
32734  * @param {Object} config The config object
32735  * @param {Roo.data.Store} store
32736  */
32737 Roo.bootstrap.PagingToolbar = function(config)
32738 {
32739     // old args format still supported... - xtype is prefered..
32740         // created from xtype...
32741     
32742     this.ds = config.dataSource;
32743     
32744     if (config.store && !this.ds) {
32745         this.store= Roo.factory(config.store, Roo.data);
32746         this.ds = this.store;
32747         this.ds.xmodule = this.xmodule || false;
32748     }
32749     
32750     this.toolbarItems = [];
32751     if (config.items) {
32752         this.toolbarItems = config.items;
32753     }
32754     
32755     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32756     
32757     this.cursor = 0;
32758     
32759     if (this.ds) { 
32760         this.bind(this.ds);
32761     }
32762     
32763     if (Roo.bootstrap.version == 4) {
32764         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32765     } else {
32766         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32767     }
32768     
32769 };
32770
32771 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32772     /**
32773      * @cfg {Roo.bootstrap.Button} buttons[]
32774      * Buttons for the toolbar
32775      */
32776      /**
32777      * @cfg {Roo.data.Store} store
32778      * The underlying data store providing the paged data
32779      */
32780     /**
32781      * @cfg {String/HTMLElement/Element} container
32782      * container The id or element that will contain the toolbar
32783      */
32784     /**
32785      * @cfg {Boolean} displayInfo
32786      * True to display the displayMsg (defaults to false)
32787      */
32788     /**
32789      * @cfg {Number} pageSize
32790      * The number of records to display per page (defaults to 20)
32791      */
32792     pageSize: 20,
32793     /**
32794      * @cfg {String} displayMsg
32795      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32796      */
32797     displayMsg : 'Displaying {0} - {1} of {2}',
32798     /**
32799      * @cfg {String} emptyMsg
32800      * The message to display when no records are found (defaults to "No data to display")
32801      */
32802     emptyMsg : 'No data to display',
32803     /**
32804      * Customizable piece of the default paging text (defaults to "Page")
32805      * @type String
32806      */
32807     beforePageText : "Page",
32808     /**
32809      * Customizable piece of the default paging text (defaults to "of %0")
32810      * @type String
32811      */
32812     afterPageText : "of {0}",
32813     /**
32814      * Customizable piece of the default paging text (defaults to "First Page")
32815      * @type String
32816      */
32817     firstText : "First Page",
32818     /**
32819      * Customizable piece of the default paging text (defaults to "Previous Page")
32820      * @type String
32821      */
32822     prevText : "Previous Page",
32823     /**
32824      * Customizable piece of the default paging text (defaults to "Next Page")
32825      * @type String
32826      */
32827     nextText : "Next Page",
32828     /**
32829      * Customizable piece of the default paging text (defaults to "Last Page")
32830      * @type String
32831      */
32832     lastText : "Last Page",
32833     /**
32834      * Customizable piece of the default paging text (defaults to "Refresh")
32835      * @type String
32836      */
32837     refreshText : "Refresh",
32838
32839     buttons : false,
32840     // private
32841     onRender : function(ct, position) 
32842     {
32843         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32844         this.navgroup.parentId = this.id;
32845         this.navgroup.onRender(this.el, null);
32846         // add the buttons to the navgroup
32847         
32848         if(this.displayInfo){
32849             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32850             this.displayEl = this.el.select('.x-paging-info', true).first();
32851 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32852 //            this.displayEl = navel.el.select('span',true).first();
32853         }
32854         
32855         var _this = this;
32856         
32857         if(this.buttons){
32858             Roo.each(_this.buttons, function(e){ // this might need to use render????
32859                Roo.factory(e).render(_this.el);
32860             });
32861         }
32862             
32863         Roo.each(_this.toolbarItems, function(e) {
32864             _this.navgroup.addItem(e);
32865         });
32866         
32867         
32868         this.first = this.navgroup.addItem({
32869             tooltip: this.firstText,
32870             cls: "prev btn-outline-secondary",
32871             html : ' <i class="fa fa-step-backward"></i>',
32872             disabled: true,
32873             preventDefault: true,
32874             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32875         });
32876         
32877         this.prev =  this.navgroup.addItem({
32878             tooltip: this.prevText,
32879             cls: "prev btn-outline-secondary",
32880             html : ' <i class="fa fa-backward"></i>',
32881             disabled: true,
32882             preventDefault: true,
32883             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32884         });
32885     //this.addSeparator();
32886         
32887         
32888         var field = this.navgroup.addItem( {
32889             tagtype : 'span',
32890             cls : 'x-paging-position  btn-outline-secondary',
32891              disabled: true,
32892             html : this.beforePageText  +
32893                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32894                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32895          } ); //?? escaped?
32896         
32897         this.field = field.el.select('input', true).first();
32898         this.field.on("keydown", this.onPagingKeydown, this);
32899         this.field.on("focus", function(){this.dom.select();});
32900     
32901     
32902         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32903         //this.field.setHeight(18);
32904         //this.addSeparator();
32905         this.next = this.navgroup.addItem({
32906             tooltip: this.nextText,
32907             cls: "next btn-outline-secondary",
32908             html : ' <i class="fa fa-forward"></i>',
32909             disabled: true,
32910             preventDefault: true,
32911             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32912         });
32913         this.last = this.navgroup.addItem({
32914             tooltip: this.lastText,
32915             html : ' <i class="fa fa-step-forward"></i>',
32916             cls: "next btn-outline-secondary",
32917             disabled: true,
32918             preventDefault: true,
32919             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32920         });
32921     //this.addSeparator();
32922         this.loading = this.navgroup.addItem({
32923             tooltip: this.refreshText,
32924             cls: "btn-outline-secondary",
32925             html : ' <i class="fa fa-refresh"></i>',
32926             preventDefault: true,
32927             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32928         });
32929         
32930     },
32931
32932     // private
32933     updateInfo : function(){
32934         if(this.displayEl){
32935             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32936             var msg = count == 0 ?
32937                 this.emptyMsg :
32938                 String.format(
32939                     this.displayMsg,
32940                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32941                 );
32942             this.displayEl.update(msg);
32943         }
32944     },
32945
32946     // private
32947     onLoad : function(ds, r, o)
32948     {
32949         this.cursor = o.params && o.params.start ? o.params.start : 0;
32950         
32951         var d = this.getPageData(),
32952             ap = d.activePage,
32953             ps = d.pages;
32954         
32955         
32956         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32957         this.field.dom.value = ap;
32958         this.first.setDisabled(ap == 1);
32959         this.prev.setDisabled(ap == 1);
32960         this.next.setDisabled(ap == ps);
32961         this.last.setDisabled(ap == ps);
32962         this.loading.enable();
32963         this.updateInfo();
32964     },
32965
32966     // private
32967     getPageData : function(){
32968         var total = this.ds.getTotalCount();
32969         return {
32970             total : total,
32971             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32972             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32973         };
32974     },
32975
32976     // private
32977     onLoadError : function(proxy, o){
32978         this.loading.enable();
32979         if (this.ds.events.loadexception.listeners.length  < 2) {
32980             // nothing has been assigned to loadexception except this...
32981             // so 
32982             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32983
32984         }
32985     },
32986
32987     // private
32988     onPagingKeydown : function(e){
32989         var k = e.getKey();
32990         var d = this.getPageData();
32991         if(k == e.RETURN){
32992             var v = this.field.dom.value, pageNum;
32993             if(!v || isNaN(pageNum = parseInt(v, 10))){
32994                 this.field.dom.value = d.activePage;
32995                 return;
32996             }
32997             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32998             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32999             e.stopEvent();
33000         }
33001         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))
33002         {
33003           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33004           this.field.dom.value = pageNum;
33005           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33006           e.stopEvent();
33007         }
33008         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33009         {
33010           var v = this.field.dom.value, pageNum; 
33011           var increment = (e.shiftKey) ? 10 : 1;
33012           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33013                 increment *= -1;
33014           }
33015           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33016             this.field.dom.value = d.activePage;
33017             return;
33018           }
33019           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33020           {
33021             this.field.dom.value = parseInt(v, 10) + increment;
33022             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33024           }
33025           e.stopEvent();
33026         }
33027     },
33028
33029     // private
33030     beforeLoad : function(){
33031         if(this.loading){
33032             this.loading.disable();
33033         }
33034     },
33035
33036     // private
33037     onClick : function(which){
33038         
33039         var ds = this.ds;
33040         if (!ds) {
33041             return;
33042         }
33043         
33044         switch(which){
33045             case "first":
33046                 ds.load({params:{start: 0, limit: this.pageSize}});
33047             break;
33048             case "prev":
33049                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33050             break;
33051             case "next":
33052                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33053             break;
33054             case "last":
33055                 var total = ds.getTotalCount();
33056                 var extra = total % this.pageSize;
33057                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33058                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33059             break;
33060             case "refresh":
33061                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33062             break;
33063         }
33064     },
33065
33066     /**
33067      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33068      * @param {Roo.data.Store} store The data store to unbind
33069      */
33070     unbind : function(ds){
33071         ds.un("beforeload", this.beforeLoad, this);
33072         ds.un("load", this.onLoad, this);
33073         ds.un("loadexception", this.onLoadError, this);
33074         ds.un("remove", this.updateInfo, this);
33075         ds.un("add", this.updateInfo, this);
33076         this.ds = undefined;
33077     },
33078
33079     /**
33080      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33081      * @param {Roo.data.Store} store The data store to bind
33082      */
33083     bind : function(ds){
33084         ds.on("beforeload", this.beforeLoad, this);
33085         ds.on("load", this.onLoad, this);
33086         ds.on("loadexception", this.onLoadError, this);
33087         ds.on("remove", this.updateInfo, this);
33088         ds.on("add", this.updateInfo, this);
33089         this.ds = ds;
33090     }
33091 });/*
33092  * - LGPL
33093  *
33094  * element
33095  * 
33096  */
33097
33098 /**
33099  * @class Roo.bootstrap.MessageBar
33100  * @extends Roo.bootstrap.Component
33101  * Bootstrap MessageBar class
33102  * @cfg {String} html contents of the MessageBar
33103  * @cfg {String} weight (info | success | warning | danger) default info
33104  * @cfg {String} beforeClass insert the bar before the given class
33105  * @cfg {Boolean} closable (true | false) default false
33106  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33107  * 
33108  * @constructor
33109  * Create a new Element
33110  * @param {Object} config The config object
33111  */
33112
33113 Roo.bootstrap.MessageBar = function(config){
33114     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33115 };
33116
33117 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33118     
33119     html: '',
33120     weight: 'info',
33121     closable: false,
33122     fixed: false,
33123     beforeClass: 'bootstrap-sticky-wrap',
33124     
33125     getAutoCreate : function(){
33126         
33127         var cfg = {
33128             tag: 'div',
33129             cls: 'alert alert-dismissable alert-' + this.weight,
33130             cn: [
33131                 {
33132                     tag: 'span',
33133                     cls: 'message',
33134                     html: this.html || ''
33135                 }
33136             ]
33137         };
33138         
33139         if(this.fixed){
33140             cfg.cls += ' alert-messages-fixed';
33141         }
33142         
33143         if(this.closable){
33144             cfg.cn.push({
33145                 tag: 'button',
33146                 cls: 'close',
33147                 html: 'x'
33148             });
33149         }
33150         
33151         return cfg;
33152     },
33153     
33154     onRender : function(ct, position)
33155     {
33156         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33157         
33158         if(!this.el){
33159             var cfg = Roo.apply({},  this.getAutoCreate());
33160             cfg.id = Roo.id();
33161             
33162             if (this.cls) {
33163                 cfg.cls += ' ' + this.cls;
33164             }
33165             if (this.style) {
33166                 cfg.style = this.style;
33167             }
33168             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33169             
33170             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33171         }
33172         
33173         this.el.select('>button.close').on('click', this.hide, this);
33174         
33175     },
33176     
33177     show : function()
33178     {
33179         if (!this.rendered) {
33180             this.render();
33181         }
33182         
33183         this.el.show();
33184         
33185         this.fireEvent('show', this);
33186         
33187     },
33188     
33189     hide : function()
33190     {
33191         if (!this.rendered) {
33192             this.render();
33193         }
33194         
33195         this.el.hide();
33196         
33197         this.fireEvent('hide', this);
33198     },
33199     
33200     update : function()
33201     {
33202 //        var e = this.el.dom.firstChild;
33203 //        
33204 //        if(this.closable){
33205 //            e = e.nextSibling;
33206 //        }
33207 //        
33208 //        e.data = this.html || '';
33209
33210         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33211     }
33212    
33213 });
33214
33215  
33216
33217      /*
33218  * - LGPL
33219  *
33220  * Graph
33221  * 
33222  */
33223
33224
33225 /**
33226  * @class Roo.bootstrap.Graph
33227  * @extends Roo.bootstrap.Component
33228  * Bootstrap Graph class
33229 > Prameters
33230  -sm {number} sm 4
33231  -md {number} md 5
33232  @cfg {String} graphtype  bar | vbar | pie
33233  @cfg {number} g_x coodinator | centre x (pie)
33234  @cfg {number} g_y coodinator | centre y (pie)
33235  @cfg {number} g_r radius (pie)
33236  @cfg {number} g_height height of the chart (respected by all elements in the set)
33237  @cfg {number} g_width width of the chart (respected by all elements in the set)
33238  @cfg {Object} title The title of the chart
33239     
33240  -{Array}  values
33241  -opts (object) options for the chart 
33242      o {
33243      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33244      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33245      o vgutter (number)
33246      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.
33247      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33248      o to
33249      o stretch (boolean)
33250      o }
33251  -opts (object) options for the pie
33252      o{
33253      o cut
33254      o startAngle (number)
33255      o endAngle (number)
33256      } 
33257  *
33258  * @constructor
33259  * Create a new Input
33260  * @param {Object} config The config object
33261  */
33262
33263 Roo.bootstrap.Graph = function(config){
33264     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33265     
33266     this.addEvents({
33267         // img events
33268         /**
33269          * @event click
33270          * The img click event for the img.
33271          * @param {Roo.EventObject} e
33272          */
33273         "click" : true
33274     });
33275 };
33276
33277 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33278     
33279     sm: 4,
33280     md: 5,
33281     graphtype: 'bar',
33282     g_height: 250,
33283     g_width: 400,
33284     g_x: 50,
33285     g_y: 50,
33286     g_r: 30,
33287     opts:{
33288         //g_colors: this.colors,
33289         g_type: 'soft',
33290         g_gutter: '20%'
33291
33292     },
33293     title : false,
33294
33295     getAutoCreate : function(){
33296         
33297         var cfg = {
33298             tag: 'div',
33299             html : null
33300         };
33301         
33302         
33303         return  cfg;
33304     },
33305
33306     onRender : function(ct,position){
33307         
33308         
33309         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33310         
33311         if (typeof(Raphael) == 'undefined') {
33312             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33313             return;
33314         }
33315         
33316         this.raphael = Raphael(this.el.dom);
33317         
33318                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33319                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33320                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33321                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33322                 /*
33323                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33324                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33325                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33326                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33327                 
33328                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33329                 r.barchart(330, 10, 300, 220, data1);
33330                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33331                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33332                 */
33333                 
33334                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33335                 // r.barchart(30, 30, 560, 250,  xdata, {
33336                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33337                 //     axis : "0 0 1 1",
33338                 //     axisxlabels :  xdata
33339                 //     //yvalues : cols,
33340                    
33341                 // });
33342 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33343 //        
33344 //        this.load(null,xdata,{
33345 //                axis : "0 0 1 1",
33346 //                axisxlabels :  xdata
33347 //                });
33348
33349     },
33350
33351     load : function(graphtype,xdata,opts)
33352     {
33353         this.raphael.clear();
33354         if(!graphtype) {
33355             graphtype = this.graphtype;
33356         }
33357         if(!opts){
33358             opts = this.opts;
33359         }
33360         var r = this.raphael,
33361             fin = function () {
33362                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33363             },
33364             fout = function () {
33365                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33366             },
33367             pfin = function() {
33368                 this.sector.stop();
33369                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33370
33371                 if (this.label) {
33372                     this.label[0].stop();
33373                     this.label[0].attr({ r: 7.5 });
33374                     this.label[1].attr({ "font-weight": 800 });
33375                 }
33376             },
33377             pfout = function() {
33378                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33379
33380                 if (this.label) {
33381                     this.label[0].animate({ r: 5 }, 500, "bounce");
33382                     this.label[1].attr({ "font-weight": 400 });
33383                 }
33384             };
33385
33386         switch(graphtype){
33387             case 'bar':
33388                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33389                 break;
33390             case 'hbar':
33391                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33392                 break;
33393             case 'pie':
33394 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33395 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33396 //            
33397                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33398                 
33399                 break;
33400
33401         }
33402         
33403         if(this.title){
33404             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33405         }
33406         
33407     },
33408     
33409     setTitle: function(o)
33410     {
33411         this.title = o;
33412     },
33413     
33414     initEvents: function() {
33415         
33416         if(!this.href){
33417             this.el.on('click', this.onClick, this);
33418         }
33419     },
33420     
33421     onClick : function(e)
33422     {
33423         Roo.log('img onclick');
33424         this.fireEvent('click', this, e);
33425     }
33426    
33427 });
33428
33429  
33430 Roo.bootstrap.dash = {};/*
33431  * - LGPL
33432  *
33433  * numberBox
33434  * 
33435  */
33436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33437
33438 /**
33439  * @class Roo.bootstrap.dash.NumberBox
33440  * @extends Roo.bootstrap.Component
33441  * Bootstrap NumberBox class
33442  * @cfg {String} headline Box headline
33443  * @cfg {String} content Box content
33444  * @cfg {String} icon Box icon
33445  * @cfg {String} footer Footer text
33446  * @cfg {String} fhref Footer href
33447  * 
33448  * @constructor
33449  * Create a new NumberBox
33450  * @param {Object} config The config object
33451  */
33452
33453
33454 Roo.bootstrap.dash.NumberBox = function(config){
33455     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33456     
33457 };
33458
33459 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33460     
33461     headline : '',
33462     content : '',
33463     icon : '',
33464     footer : '',
33465     fhref : '',
33466     ficon : '',
33467     
33468     getAutoCreate : function(){
33469         
33470         var cfg = {
33471             tag : 'div',
33472             cls : 'small-box ',
33473             cn : [
33474                 {
33475                     tag : 'div',
33476                     cls : 'inner',
33477                     cn :[
33478                         {
33479                             tag : 'h3',
33480                             cls : 'roo-headline',
33481                             html : this.headline
33482                         },
33483                         {
33484                             tag : 'p',
33485                             cls : 'roo-content',
33486                             html : this.content
33487                         }
33488                     ]
33489                 }
33490             ]
33491         };
33492         
33493         if(this.icon){
33494             cfg.cn.push({
33495                 tag : 'div',
33496                 cls : 'icon',
33497                 cn :[
33498                     {
33499                         tag : 'i',
33500                         cls : 'ion ' + this.icon
33501                     }
33502                 ]
33503             });
33504         }
33505         
33506         if(this.footer){
33507             var footer = {
33508                 tag : 'a',
33509                 cls : 'small-box-footer',
33510                 href : this.fhref || '#',
33511                 html : this.footer
33512             };
33513             
33514             cfg.cn.push(footer);
33515             
33516         }
33517         
33518         return  cfg;
33519     },
33520
33521     onRender : function(ct,position){
33522         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33523
33524
33525        
33526                 
33527     },
33528
33529     setHeadline: function (value)
33530     {
33531         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33532     },
33533     
33534     setFooter: function (value, href)
33535     {
33536         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33537         
33538         if(href){
33539             this.el.select('a.small-box-footer',true).first().attr('href', href);
33540         }
33541         
33542     },
33543
33544     setContent: function (value)
33545     {
33546         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33547     },
33548
33549     initEvents: function() 
33550     {   
33551         
33552     }
33553     
33554 });
33555
33556  
33557 /*
33558  * - LGPL
33559  *
33560  * TabBox
33561  * 
33562  */
33563 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33564
33565 /**
33566  * @class Roo.bootstrap.dash.TabBox
33567  * @extends Roo.bootstrap.Component
33568  * @children Roo.bootstrap.dash.TabPane
33569  * Bootstrap TabBox class
33570  * @cfg {String} title Title of the TabBox
33571  * @cfg {String} icon Icon of the TabBox
33572  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33573  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33574  * 
33575  * @constructor
33576  * Create a new TabBox
33577  * @param {Object} config The config object
33578  */
33579
33580
33581 Roo.bootstrap.dash.TabBox = function(config){
33582     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33583     this.addEvents({
33584         // raw events
33585         /**
33586          * @event addpane
33587          * When a pane is added
33588          * @param {Roo.bootstrap.dash.TabPane} pane
33589          */
33590         "addpane" : true,
33591         /**
33592          * @event activatepane
33593          * When a pane is activated
33594          * @param {Roo.bootstrap.dash.TabPane} pane
33595          */
33596         "activatepane" : true
33597         
33598          
33599     });
33600     
33601     this.panes = [];
33602 };
33603
33604 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33605
33606     title : '',
33607     icon : false,
33608     showtabs : true,
33609     tabScrollable : false,
33610     
33611     getChildContainer : function()
33612     {
33613         return this.el.select('.tab-content', true).first();
33614     },
33615     
33616     getAutoCreate : function(){
33617         
33618         var header = {
33619             tag: 'li',
33620             cls: 'pull-left header',
33621             html: this.title,
33622             cn : []
33623         };
33624         
33625         if(this.icon){
33626             header.cn.push({
33627                 tag: 'i',
33628                 cls: 'fa ' + this.icon
33629             });
33630         }
33631         
33632         var h = {
33633             tag: 'ul',
33634             cls: 'nav nav-tabs pull-right',
33635             cn: [
33636                 header
33637             ]
33638         };
33639         
33640         if(this.tabScrollable){
33641             h = {
33642                 tag: 'div',
33643                 cls: 'tab-header',
33644                 cn: [
33645                     {
33646                         tag: 'ul',
33647                         cls: 'nav nav-tabs pull-right',
33648                         cn: [
33649                             header
33650                         ]
33651                     }
33652                 ]
33653             };
33654         }
33655         
33656         var cfg = {
33657             tag: 'div',
33658             cls: 'nav-tabs-custom',
33659             cn: [
33660                 h,
33661                 {
33662                     tag: 'div',
33663                     cls: 'tab-content no-padding',
33664                     cn: []
33665                 }
33666             ]
33667         };
33668
33669         return  cfg;
33670     },
33671     initEvents : function()
33672     {
33673         //Roo.log('add add pane handler');
33674         this.on('addpane', this.onAddPane, this);
33675     },
33676      /**
33677      * Updates the box title
33678      * @param {String} html to set the title to.
33679      */
33680     setTitle : function(value)
33681     {
33682         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33683     },
33684     onAddPane : function(pane)
33685     {
33686         this.panes.push(pane);
33687         //Roo.log('addpane');
33688         //Roo.log(pane);
33689         // tabs are rendere left to right..
33690         if(!this.showtabs){
33691             return;
33692         }
33693         
33694         var ctr = this.el.select('.nav-tabs', true).first();
33695          
33696          
33697         var existing = ctr.select('.nav-tab',true);
33698         var qty = existing.getCount();;
33699         
33700         
33701         var tab = ctr.createChild({
33702             tag : 'li',
33703             cls : 'nav-tab' + (qty ? '' : ' active'),
33704             cn : [
33705                 {
33706                     tag : 'a',
33707                     href:'#',
33708                     html : pane.title
33709                 }
33710             ]
33711         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33712         pane.tab = tab;
33713         
33714         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33715         if (!qty) {
33716             pane.el.addClass('active');
33717         }
33718         
33719                 
33720     },
33721     onTabClick : function(ev,un,ob,pane)
33722     {
33723         //Roo.log('tab - prev default');
33724         ev.preventDefault();
33725         
33726         
33727         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33728         pane.tab.addClass('active');
33729         //Roo.log(pane.title);
33730         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33731         // technically we should have a deactivate event.. but maybe add later.
33732         // and it should not de-activate the selected tab...
33733         this.fireEvent('activatepane', pane);
33734         pane.el.addClass('active');
33735         pane.fireEvent('activate');
33736         
33737         
33738     },
33739     
33740     getActivePane : function()
33741     {
33742         var r = false;
33743         Roo.each(this.panes, function(p) {
33744             if(p.el.hasClass('active')){
33745                 r = p;
33746                 return false;
33747             }
33748             
33749             return;
33750         });
33751         
33752         return r;
33753     }
33754     
33755     
33756 });
33757
33758  
33759 /*
33760  * - LGPL
33761  *
33762  * Tab pane
33763  * 
33764  */
33765 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33766 /**
33767  * @class Roo.bootstrap.TabPane
33768  * @extends Roo.bootstrap.Component
33769  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33770  * Bootstrap TabPane class
33771  * @cfg {Boolean} active (false | true) Default false
33772  * @cfg {String} title title of panel
33773
33774  * 
33775  * @constructor
33776  * Create a new TabPane
33777  * @param {Object} config The config object
33778  */
33779
33780 Roo.bootstrap.dash.TabPane = function(config){
33781     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33782     
33783     this.addEvents({
33784         // raw events
33785         /**
33786          * @event activate
33787          * When a pane is activated
33788          * @param {Roo.bootstrap.dash.TabPane} pane
33789          */
33790         "activate" : true
33791          
33792     });
33793 };
33794
33795 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33796     
33797     active : false,
33798     title : '',
33799     
33800     // the tabBox that this is attached to.
33801     tab : false,
33802      
33803     getAutoCreate : function() 
33804     {
33805         var cfg = {
33806             tag: 'div',
33807             cls: 'tab-pane'
33808         };
33809         
33810         if(this.active){
33811             cfg.cls += ' active';
33812         }
33813         
33814         return cfg;
33815     },
33816     initEvents  : function()
33817     {
33818         //Roo.log('trigger add pane handler');
33819         this.parent().fireEvent('addpane', this)
33820     },
33821     
33822      /**
33823      * Updates the tab title 
33824      * @param {String} html to set the title to.
33825      */
33826     setTitle: function(str)
33827     {
33828         if (!this.tab) {
33829             return;
33830         }
33831         this.title = str;
33832         this.tab.select('a', true).first().dom.innerHTML = str;
33833         
33834     }
33835     
33836     
33837     
33838 });
33839
33840  
33841
33842
33843  /*
33844  * - LGPL
33845  *
33846  * Tooltip
33847  * 
33848  */
33849
33850 /**
33851  * @class Roo.bootstrap.Tooltip
33852  * Bootstrap Tooltip class
33853  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33854  * to determine which dom element triggers the tooltip.
33855  * 
33856  * It needs to add support for additional attributes like tooltip-position
33857  * 
33858  * @constructor
33859  * Create a new Toolti
33860  * @param {Object} config The config object
33861  */
33862
33863 Roo.bootstrap.Tooltip = function(config){
33864     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33865     
33866     this.alignment = Roo.bootstrap.Tooltip.alignment;
33867     
33868     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33869         this.alignment = config.alignment;
33870     }
33871     
33872 };
33873
33874 Roo.apply(Roo.bootstrap.Tooltip, {
33875     /**
33876      * @function init initialize tooltip monitoring.
33877      * @static
33878      */
33879     currentEl : false,
33880     currentTip : false,
33881     currentRegion : false,
33882     
33883     //  init : delay?
33884     
33885     init : function()
33886     {
33887         Roo.get(document).on('mouseover', this.enter ,this);
33888         Roo.get(document).on('mouseout', this.leave, this);
33889          
33890         
33891         this.currentTip = new Roo.bootstrap.Tooltip();
33892     },
33893     
33894     enter : function(ev)
33895     {
33896         var dom = ev.getTarget();
33897         
33898         //Roo.log(['enter',dom]);
33899         var el = Roo.fly(dom);
33900         if (this.currentEl) {
33901             //Roo.log(dom);
33902             //Roo.log(this.currentEl);
33903             //Roo.log(this.currentEl.contains(dom));
33904             if (this.currentEl == el) {
33905                 return;
33906             }
33907             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33908                 return;
33909             }
33910
33911         }
33912         
33913         if (this.currentTip.el) {
33914             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33915         }    
33916         //Roo.log(ev);
33917         
33918         if(!el || el.dom == document){
33919             return;
33920         }
33921         
33922         var bindEl = el; 
33923         var pel = false;
33924         if (!el.attr('tooltip')) {
33925             pel = el.findParent("[tooltip]");
33926             if (pel) {
33927                 bindEl = Roo.get(pel);
33928             }
33929         }
33930         
33931        
33932         
33933         // you can not look for children, as if el is the body.. then everythign is the child..
33934         if (!pel && !el.attr('tooltip')) { //
33935             if (!el.select("[tooltip]").elements.length) {
33936                 return;
33937             }
33938             // is the mouse over this child...?
33939             bindEl = el.select("[tooltip]").first();
33940             var xy = ev.getXY();
33941             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33942                 //Roo.log("not in region.");
33943                 return;
33944             }
33945             //Roo.log("child element over..");
33946             
33947         }
33948         this.currentEl = el;
33949         this.currentTip.bind(bindEl);
33950         this.currentRegion = Roo.lib.Region.getRegion(dom);
33951         this.currentTip.enter();
33952         
33953     },
33954     leave : function(ev)
33955     {
33956         var dom = ev.getTarget();
33957         //Roo.log(['leave',dom]);
33958         if (!this.currentEl) {
33959             return;
33960         }
33961         
33962         
33963         if (dom != this.currentEl.dom) {
33964             return;
33965         }
33966         var xy = ev.getXY();
33967         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33968             return;
33969         }
33970         // only activate leave if mouse cursor is outside... bounding box..
33971         
33972         
33973         
33974         
33975         if (this.currentTip) {
33976             this.currentTip.leave();
33977         }
33978         //Roo.log('clear currentEl');
33979         this.currentEl = false;
33980         
33981         
33982     },
33983     alignment : {
33984         'left' : ['r-l', [-2,0], 'right'],
33985         'right' : ['l-r', [2,0], 'left'],
33986         'bottom' : ['t-b', [0,2], 'top'],
33987         'top' : [ 'b-t', [0,-2], 'bottom']
33988     }
33989     
33990 });
33991
33992
33993 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33994     
33995     
33996     bindEl : false,
33997     
33998     delay : null, // can be { show : 300 , hide: 500}
33999     
34000     timeout : null,
34001     
34002     hoverState : null, //???
34003     
34004     placement : 'bottom', 
34005     
34006     alignment : false,
34007     
34008     getAutoCreate : function(){
34009     
34010         var cfg = {
34011            cls : 'tooltip',   
34012            role : 'tooltip',
34013            cn : [
34014                 {
34015                     cls : 'tooltip-arrow arrow'
34016                 },
34017                 {
34018                     cls : 'tooltip-inner'
34019                 }
34020            ]
34021         };
34022         
34023         return cfg;
34024     },
34025     bind : function(el)
34026     {
34027         this.bindEl = el;
34028     },
34029     
34030     initEvents : function()
34031     {
34032         this.arrowEl = this.el.select('.arrow', true).first();
34033         this.innerEl = this.el.select('.tooltip-inner', true).first();
34034     },
34035     
34036     enter : function () {
34037        
34038         if (this.timeout != null) {
34039             clearTimeout(this.timeout);
34040         }
34041         
34042         this.hoverState = 'in';
34043          //Roo.log("enter - show");
34044         if (!this.delay || !this.delay.show) {
34045             this.show();
34046             return;
34047         }
34048         var _t = this;
34049         this.timeout = setTimeout(function () {
34050             if (_t.hoverState == 'in') {
34051                 _t.show();
34052             }
34053         }, this.delay.show);
34054     },
34055     leave : function()
34056     {
34057         clearTimeout(this.timeout);
34058     
34059         this.hoverState = 'out';
34060          if (!this.delay || !this.delay.hide) {
34061             this.hide();
34062             return;
34063         }
34064        
34065         var _t = this;
34066         this.timeout = setTimeout(function () {
34067             //Roo.log("leave - timeout");
34068             
34069             if (_t.hoverState == 'out') {
34070                 _t.hide();
34071                 Roo.bootstrap.Tooltip.currentEl = false;
34072             }
34073         }, delay);
34074     },
34075     
34076     show : function (msg)
34077     {
34078         if (!this.el) {
34079             this.render(document.body);
34080         }
34081         // set content.
34082         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34083         
34084         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34085         
34086         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34087         
34088         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34089                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34090
34091         if(this.bindEl.attr('tooltip-class')) {
34092             this.el.addClass(this.bindEl.attr('tooltip-class'));
34093         }
34094         
34095         var placement = typeof this.placement == 'function' ?
34096             this.placement.call(this, this.el, on_el) :
34097             this.placement;
34098         
34099         if(this.bindEl.attr('tooltip-placement')) {
34100             placement = this.bindEl.attr('tooltip-placement');
34101         }
34102             
34103         var autoToken = /\s?auto?\s?/i;
34104         var autoPlace = autoToken.test(placement);
34105         if (autoPlace) {
34106             placement = placement.replace(autoToken, '') || 'top';
34107         }
34108         
34109         //this.el.detach()
34110         //this.el.setXY([0,0]);
34111         this.el.show();
34112         //this.el.dom.style.display='block';
34113         
34114         //this.el.appendTo(on_el);
34115         
34116         var p = this.getPosition();
34117         var box = this.el.getBox();
34118         
34119         if (autoPlace) {
34120             // fixme..
34121         }
34122         
34123         var align = this.alignment[placement];
34124         
34125         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34126         
34127         if(placement == 'top' || placement == 'bottom'){
34128             if(xy[0] < 0){
34129                 placement = 'right';
34130             }
34131             
34132             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34133                 placement = 'left';
34134             }
34135             
34136             var scroll = Roo.select('body', true).first().getScroll();
34137             
34138             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34139                 placement = 'top';
34140             }
34141             
34142             align = this.alignment[placement];
34143             
34144             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34145             
34146         }
34147         
34148         var elems = document.getElementsByTagName('div');
34149         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34150         for (var i = 0; i < elems.length; i++) {
34151           var zindex = Number.parseInt(
34152                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34153                 10
34154           );
34155           if (zindex > highest) {
34156             highest = zindex;
34157           }
34158         }
34159         
34160         
34161         
34162         this.el.dom.style.zIndex = highest;
34163         
34164         this.el.alignTo(this.bindEl, align[0],align[1]);
34165         //var arrow = this.el.select('.arrow',true).first();
34166         //arrow.set(align[2], 
34167         
34168         this.el.addClass(placement);
34169         this.el.addClass("bs-tooltip-"+ placement);
34170         
34171         this.el.addClass('in fade show');
34172         
34173         this.hoverState = null;
34174         
34175         if (this.el.hasClass('fade')) {
34176             // fade it?
34177         }
34178         
34179         
34180         
34181         
34182         
34183     },
34184     hide : function()
34185     {
34186          
34187         if (!this.el) {
34188             return;
34189         }
34190         //this.el.setXY([0,0]);
34191         if(this.bindEl.attr('tooltip-class')) {
34192             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34193         }
34194         this.el.removeClass(['show', 'in']);
34195         //this.el.hide();
34196         
34197     }
34198     
34199 });
34200  
34201
34202  /*
34203  * - LGPL
34204  *
34205  * Location Picker
34206  * 
34207  */
34208
34209 /**
34210  * @class Roo.bootstrap.LocationPicker
34211  * @extends Roo.bootstrap.Component
34212  * Bootstrap LocationPicker class
34213  * @cfg {Number} latitude Position when init default 0
34214  * @cfg {Number} longitude Position when init default 0
34215  * @cfg {Number} zoom default 15
34216  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34217  * @cfg {Boolean} mapTypeControl default false
34218  * @cfg {Boolean} disableDoubleClickZoom default false
34219  * @cfg {Boolean} scrollwheel default true
34220  * @cfg {Boolean} streetViewControl default false
34221  * @cfg {Number} radius default 0
34222  * @cfg {String} locationName
34223  * @cfg {Boolean} draggable default true
34224  * @cfg {Boolean} enableAutocomplete default false
34225  * @cfg {Boolean} enableReverseGeocode default true
34226  * @cfg {String} markerTitle
34227  * 
34228  * @constructor
34229  * Create a new LocationPicker
34230  * @param {Object} config The config object
34231  */
34232
34233
34234 Roo.bootstrap.LocationPicker = function(config){
34235     
34236     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34237     
34238     this.addEvents({
34239         /**
34240          * @event initial
34241          * Fires when the picker initialized.
34242          * @param {Roo.bootstrap.LocationPicker} this
34243          * @param {Google Location} location
34244          */
34245         initial : true,
34246         /**
34247          * @event positionchanged
34248          * Fires when the picker position changed.
34249          * @param {Roo.bootstrap.LocationPicker} this
34250          * @param {Google Location} location
34251          */
34252         positionchanged : true,
34253         /**
34254          * @event resize
34255          * Fires when the map resize.
34256          * @param {Roo.bootstrap.LocationPicker} this
34257          */
34258         resize : true,
34259         /**
34260          * @event show
34261          * Fires when the map show.
34262          * @param {Roo.bootstrap.LocationPicker} this
34263          */
34264         show : true,
34265         /**
34266          * @event hide
34267          * Fires when the map hide.
34268          * @param {Roo.bootstrap.LocationPicker} this
34269          */
34270         hide : true,
34271         /**
34272          * @event mapClick
34273          * Fires when click the map.
34274          * @param {Roo.bootstrap.LocationPicker} this
34275          * @param {Map event} e
34276          */
34277         mapClick : true,
34278         /**
34279          * @event mapRightClick
34280          * Fires when right click the map.
34281          * @param {Roo.bootstrap.LocationPicker} this
34282          * @param {Map event} e
34283          */
34284         mapRightClick : true,
34285         /**
34286          * @event markerClick
34287          * Fires when click the marker.
34288          * @param {Roo.bootstrap.LocationPicker} this
34289          * @param {Map event} e
34290          */
34291         markerClick : true,
34292         /**
34293          * @event markerRightClick
34294          * Fires when right click the marker.
34295          * @param {Roo.bootstrap.LocationPicker} this
34296          * @param {Map event} e
34297          */
34298         markerRightClick : true,
34299         /**
34300          * @event OverlayViewDraw
34301          * Fires when OverlayView Draw
34302          * @param {Roo.bootstrap.LocationPicker} this
34303          */
34304         OverlayViewDraw : true,
34305         /**
34306          * @event OverlayViewOnAdd
34307          * Fires when OverlayView Draw
34308          * @param {Roo.bootstrap.LocationPicker} this
34309          */
34310         OverlayViewOnAdd : true,
34311         /**
34312          * @event OverlayViewOnRemove
34313          * Fires when OverlayView Draw
34314          * @param {Roo.bootstrap.LocationPicker} this
34315          */
34316         OverlayViewOnRemove : true,
34317         /**
34318          * @event OverlayViewShow
34319          * Fires when OverlayView Draw
34320          * @param {Roo.bootstrap.LocationPicker} this
34321          * @param {Pixel} cpx
34322          */
34323         OverlayViewShow : true,
34324         /**
34325          * @event OverlayViewHide
34326          * Fires when OverlayView Draw
34327          * @param {Roo.bootstrap.LocationPicker} this
34328          */
34329         OverlayViewHide : true,
34330         /**
34331          * @event loadexception
34332          * Fires when load google lib failed.
34333          * @param {Roo.bootstrap.LocationPicker} this
34334          */
34335         loadexception : true
34336     });
34337         
34338 };
34339
34340 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34341     
34342     gMapContext: false,
34343     
34344     latitude: 0,
34345     longitude: 0,
34346     zoom: 15,
34347     mapTypeId: false,
34348     mapTypeControl: false,
34349     disableDoubleClickZoom: false,
34350     scrollwheel: true,
34351     streetViewControl: false,
34352     radius: 0,
34353     locationName: '',
34354     draggable: true,
34355     enableAutocomplete: false,
34356     enableReverseGeocode: true,
34357     markerTitle: '',
34358     
34359     getAutoCreate: function()
34360     {
34361
34362         var cfg = {
34363             tag: 'div',
34364             cls: 'roo-location-picker'
34365         };
34366         
34367         return cfg
34368     },
34369     
34370     initEvents: function(ct, position)
34371     {       
34372         if(!this.el.getWidth() || this.isApplied()){
34373             return;
34374         }
34375         
34376         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34377         
34378         this.initial();
34379     },
34380     
34381     initial: function()
34382     {
34383         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34384             this.fireEvent('loadexception', this);
34385             return;
34386         }
34387         
34388         if(!this.mapTypeId){
34389             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34390         }
34391         
34392         this.gMapContext = this.GMapContext();
34393         
34394         this.initOverlayView();
34395         
34396         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34397         
34398         var _this = this;
34399                 
34400         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34401             _this.setPosition(_this.gMapContext.marker.position);
34402         });
34403         
34404         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34405             _this.fireEvent('mapClick', this, event);
34406             
34407         });
34408
34409         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34410             _this.fireEvent('mapRightClick', this, event);
34411             
34412         });
34413         
34414         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34415             _this.fireEvent('markerClick', this, event);
34416             
34417         });
34418
34419         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34420             _this.fireEvent('markerRightClick', this, event);
34421             
34422         });
34423         
34424         this.setPosition(this.gMapContext.location);
34425         
34426         this.fireEvent('initial', this, this.gMapContext.location);
34427     },
34428     
34429     initOverlayView: function()
34430     {
34431         var _this = this;
34432         
34433         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34434             
34435             draw: function()
34436             {
34437                 _this.fireEvent('OverlayViewDraw', _this);
34438             },
34439             
34440             onAdd: function()
34441             {
34442                 _this.fireEvent('OverlayViewOnAdd', _this);
34443             },
34444             
34445             onRemove: function()
34446             {
34447                 _this.fireEvent('OverlayViewOnRemove', _this);
34448             },
34449             
34450             show: function(cpx)
34451             {
34452                 _this.fireEvent('OverlayViewShow', _this, cpx);
34453             },
34454             
34455             hide: function()
34456             {
34457                 _this.fireEvent('OverlayViewHide', _this);
34458             }
34459             
34460         });
34461     },
34462     
34463     fromLatLngToContainerPixel: function(event)
34464     {
34465         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34466     },
34467     
34468     isApplied: function() 
34469     {
34470         return this.getGmapContext() == false ? false : true;
34471     },
34472     
34473     getGmapContext: function() 
34474     {
34475         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34476     },
34477     
34478     GMapContext: function() 
34479     {
34480         var position = new google.maps.LatLng(this.latitude, this.longitude);
34481         
34482         var _map = new google.maps.Map(this.el.dom, {
34483             center: position,
34484             zoom: this.zoom,
34485             mapTypeId: this.mapTypeId,
34486             mapTypeControl: this.mapTypeControl,
34487             disableDoubleClickZoom: this.disableDoubleClickZoom,
34488             scrollwheel: this.scrollwheel,
34489             streetViewControl: this.streetViewControl,
34490             locationName: this.locationName,
34491             draggable: this.draggable,
34492             enableAutocomplete: this.enableAutocomplete,
34493             enableReverseGeocode: this.enableReverseGeocode
34494         });
34495         
34496         var _marker = new google.maps.Marker({
34497             position: position,
34498             map: _map,
34499             title: this.markerTitle,
34500             draggable: this.draggable
34501         });
34502         
34503         return {
34504             map: _map,
34505             marker: _marker,
34506             circle: null,
34507             location: position,
34508             radius: this.radius,
34509             locationName: this.locationName,
34510             addressComponents: {
34511                 formatted_address: null,
34512                 addressLine1: null,
34513                 addressLine2: null,
34514                 streetName: null,
34515                 streetNumber: null,
34516                 city: null,
34517                 district: null,
34518                 state: null,
34519                 stateOrProvince: null
34520             },
34521             settings: this,
34522             domContainer: this.el.dom,
34523             geodecoder: new google.maps.Geocoder()
34524         };
34525     },
34526     
34527     drawCircle: function(center, radius, options) 
34528     {
34529         if (this.gMapContext.circle != null) {
34530             this.gMapContext.circle.setMap(null);
34531         }
34532         if (radius > 0) {
34533             radius *= 1;
34534             options = Roo.apply({}, options, {
34535                 strokeColor: "#0000FF",
34536                 strokeOpacity: .35,
34537                 strokeWeight: 2,
34538                 fillColor: "#0000FF",
34539                 fillOpacity: .2
34540             });
34541             
34542             options.map = this.gMapContext.map;
34543             options.radius = radius;
34544             options.center = center;
34545             this.gMapContext.circle = new google.maps.Circle(options);
34546             return this.gMapContext.circle;
34547         }
34548         
34549         return null;
34550     },
34551     
34552     setPosition: function(location) 
34553     {
34554         this.gMapContext.location = location;
34555         this.gMapContext.marker.setPosition(location);
34556         this.gMapContext.map.panTo(location);
34557         this.drawCircle(location, this.gMapContext.radius, {});
34558         
34559         var _this = this;
34560         
34561         if (this.gMapContext.settings.enableReverseGeocode) {
34562             this.gMapContext.geodecoder.geocode({
34563                 latLng: this.gMapContext.location
34564             }, function(results, status) {
34565                 
34566                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34567                     _this.gMapContext.locationName = results[0].formatted_address;
34568                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34569                     
34570                     _this.fireEvent('positionchanged', this, location);
34571                 }
34572             });
34573             
34574             return;
34575         }
34576         
34577         this.fireEvent('positionchanged', this, location);
34578     },
34579     
34580     resize: function()
34581     {
34582         google.maps.event.trigger(this.gMapContext.map, "resize");
34583         
34584         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34585         
34586         this.fireEvent('resize', this);
34587     },
34588     
34589     setPositionByLatLng: function(latitude, longitude)
34590     {
34591         this.setPosition(new google.maps.LatLng(latitude, longitude));
34592     },
34593     
34594     getCurrentPosition: function() 
34595     {
34596         return {
34597             latitude: this.gMapContext.location.lat(),
34598             longitude: this.gMapContext.location.lng()
34599         };
34600     },
34601     
34602     getAddressName: function() 
34603     {
34604         return this.gMapContext.locationName;
34605     },
34606     
34607     getAddressComponents: function() 
34608     {
34609         return this.gMapContext.addressComponents;
34610     },
34611     
34612     address_component_from_google_geocode: function(address_components) 
34613     {
34614         var result = {};
34615         
34616         for (var i = 0; i < address_components.length; i++) {
34617             var component = address_components[i];
34618             if (component.types.indexOf("postal_code") >= 0) {
34619                 result.postalCode = component.short_name;
34620             } else if (component.types.indexOf("street_number") >= 0) {
34621                 result.streetNumber = component.short_name;
34622             } else if (component.types.indexOf("route") >= 0) {
34623                 result.streetName = component.short_name;
34624             } else if (component.types.indexOf("neighborhood") >= 0) {
34625                 result.city = component.short_name;
34626             } else if (component.types.indexOf("locality") >= 0) {
34627                 result.city = component.short_name;
34628             } else if (component.types.indexOf("sublocality") >= 0) {
34629                 result.district = component.short_name;
34630             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34631                 result.stateOrProvince = component.short_name;
34632             } else if (component.types.indexOf("country") >= 0) {
34633                 result.country = component.short_name;
34634             }
34635         }
34636         
34637         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34638         result.addressLine2 = "";
34639         return result;
34640     },
34641     
34642     setZoomLevel: function(zoom)
34643     {
34644         this.gMapContext.map.setZoom(zoom);
34645     },
34646     
34647     show: function()
34648     {
34649         if(!this.el){
34650             return;
34651         }
34652         
34653         this.el.show();
34654         
34655         this.resize();
34656         
34657         this.fireEvent('show', this);
34658     },
34659     
34660     hide: function()
34661     {
34662         if(!this.el){
34663             return;
34664         }
34665         
34666         this.el.hide();
34667         
34668         this.fireEvent('hide', this);
34669     }
34670     
34671 });
34672
34673 Roo.apply(Roo.bootstrap.LocationPicker, {
34674     
34675     OverlayView : function(map, options)
34676     {
34677         options = options || {};
34678         
34679         this.setMap(map);
34680     }
34681     
34682     
34683 });/**
34684  * @class Roo.bootstrap.Alert
34685  * @extends Roo.bootstrap.Component
34686  * Bootstrap Alert class - shows an alert area box
34687  * eg
34688  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34689   Enter a valid email address
34690 </div>
34691  * @licence LGPL
34692  * @cfg {String} title The title of alert
34693  * @cfg {String} html The content of alert
34694  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34695  * @cfg {String} fa font-awesomeicon
34696  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34697  * @cfg {Boolean} close true to show a x closer
34698  * 
34699  * 
34700  * @constructor
34701  * Create a new alert
34702  * @param {Object} config The config object
34703  */
34704
34705
34706 Roo.bootstrap.Alert = function(config){
34707     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34708     
34709 };
34710
34711 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34712     
34713     title: '',
34714     html: '',
34715     weight: false,
34716     fa: false,
34717     faicon: false, // BC
34718     close : false,
34719     
34720     
34721     getAutoCreate : function()
34722     {
34723         
34724         var cfg = {
34725             tag : 'div',
34726             cls : 'alert',
34727             cn : [
34728                 {
34729                     tag: 'button',
34730                     type :  "button",
34731                     cls: "close",
34732                     html : '×',
34733                     style : this.close ? '' : 'display:none'
34734                 },
34735                 {
34736                     tag : 'i',
34737                     cls : 'roo-alert-icon'
34738                     
34739                 },
34740                 {
34741                     tag : 'b',
34742                     cls : 'roo-alert-title',
34743                     html : this.title
34744                 },
34745                 {
34746                     tag : 'span',
34747                     cls : 'roo-alert-text',
34748                     html : this.html
34749                 }
34750             ]
34751         };
34752         
34753         if(this.faicon){
34754             cfg.cn[0].cls += ' fa ' + this.faicon;
34755         }
34756         if(this.fa){
34757             cfg.cn[0].cls += ' fa ' + this.fa;
34758         }
34759         
34760         if(this.weight){
34761             cfg.cls += ' alert-' + this.weight;
34762         }
34763         
34764         return cfg;
34765     },
34766     
34767     initEvents: function() 
34768     {
34769         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34770         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34771         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34772         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34773         if (this.seconds > 0) {
34774             this.hide.defer(this.seconds, this);
34775         }
34776     },
34777     /**
34778      * Set the Title Message HTML
34779      * @param {String} html
34780      */
34781     setTitle : function(str)
34782     {
34783         this.titleEl.dom.innerHTML = str;
34784     },
34785      
34786      /**
34787      * Set the Body Message HTML
34788      * @param {String} html
34789      */
34790     setHtml : function(str)
34791     {
34792         this.htmlEl.dom.innerHTML = str;
34793     },
34794     /**
34795      * Set the Weight of the alert
34796      * @param {String} (success|info|warning|danger) weight
34797      */
34798     
34799     setWeight : function(weight)
34800     {
34801         if(this.weight){
34802             this.el.removeClass('alert-' + this.weight);
34803         }
34804         
34805         this.weight = weight;
34806         
34807         this.el.addClass('alert-' + this.weight);
34808     },
34809       /**
34810      * Set the Icon of the alert
34811      * @param {String} see fontawsome names (name without the 'fa-' bit)
34812      */
34813     setIcon : function(icon)
34814     {
34815         if(this.faicon){
34816             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34817         }
34818         
34819         this.faicon = icon;
34820         
34821         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34822     },
34823     /**
34824      * Hide the Alert
34825      */
34826     hide: function() 
34827     {
34828         this.el.hide();   
34829     },
34830     /**
34831      * Show the Alert
34832      */
34833     show: function() 
34834     {  
34835         this.el.show();   
34836     }
34837     
34838 });
34839
34840  
34841 /*
34842 * Licence: LGPL
34843 */
34844
34845 /**
34846  * @class Roo.bootstrap.UploadCropbox
34847  * @extends Roo.bootstrap.Component
34848  * Bootstrap UploadCropbox class
34849  * @cfg {String} emptyText show when image has been loaded
34850  * @cfg {String} rotateNotify show when image too small to rotate
34851  * @cfg {Number} errorTimeout default 3000
34852  * @cfg {Number} minWidth default 300
34853  * @cfg {Number} minHeight default 300
34854  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34855  * @cfg {Boolean} isDocument (true|false) default false
34856  * @cfg {String} url action url
34857  * @cfg {String} paramName default 'imageUpload'
34858  * @cfg {String} method default POST
34859  * @cfg {Boolean} loadMask (true|false) default true
34860  * @cfg {Boolean} loadingText default 'Loading...'
34861  * 
34862  * @constructor
34863  * Create a new UploadCropbox
34864  * @param {Object} config The config object
34865  */
34866
34867 Roo.bootstrap.UploadCropbox = function(config){
34868     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34869     
34870     this.addEvents({
34871         /**
34872          * @event beforeselectfile
34873          * Fire before select file
34874          * @param {Roo.bootstrap.UploadCropbox} this
34875          */
34876         "beforeselectfile" : true,
34877         /**
34878          * @event initial
34879          * Fire after initEvent
34880          * @param {Roo.bootstrap.UploadCropbox} this
34881          */
34882         "initial" : true,
34883         /**
34884          * @event crop
34885          * Fire after initEvent
34886          * @param {Roo.bootstrap.UploadCropbox} this
34887          * @param {String} data
34888          */
34889         "crop" : true,
34890         /**
34891          * @event prepare
34892          * Fire when preparing the file data
34893          * @param {Roo.bootstrap.UploadCropbox} this
34894          * @param {Object} file
34895          */
34896         "prepare" : true,
34897         /**
34898          * @event exception
34899          * Fire when get exception
34900          * @param {Roo.bootstrap.UploadCropbox} this
34901          * @param {XMLHttpRequest} xhr
34902          */
34903         "exception" : true,
34904         /**
34905          * @event beforeloadcanvas
34906          * Fire before load the canvas
34907          * @param {Roo.bootstrap.UploadCropbox} this
34908          * @param {String} src
34909          */
34910         "beforeloadcanvas" : true,
34911         /**
34912          * @event trash
34913          * Fire when trash image
34914          * @param {Roo.bootstrap.UploadCropbox} this
34915          */
34916         "trash" : true,
34917         /**
34918          * @event download
34919          * Fire when download the image
34920          * @param {Roo.bootstrap.UploadCropbox} this
34921          */
34922         "download" : true,
34923         /**
34924          * @event footerbuttonclick
34925          * Fire when footerbuttonclick
34926          * @param {Roo.bootstrap.UploadCropbox} this
34927          * @param {String} type
34928          */
34929         "footerbuttonclick" : true,
34930         /**
34931          * @event resize
34932          * Fire when resize
34933          * @param {Roo.bootstrap.UploadCropbox} this
34934          */
34935         "resize" : true,
34936         /**
34937          * @event rotate
34938          * Fire when rotate the image
34939          * @param {Roo.bootstrap.UploadCropbox} this
34940          * @param {String} pos
34941          */
34942         "rotate" : true,
34943         /**
34944          * @event inspect
34945          * Fire when inspect the file
34946          * @param {Roo.bootstrap.UploadCropbox} this
34947          * @param {Object} file
34948          */
34949         "inspect" : true,
34950         /**
34951          * @event upload
34952          * Fire when xhr upload the file
34953          * @param {Roo.bootstrap.UploadCropbox} this
34954          * @param {Object} data
34955          */
34956         "upload" : true,
34957         /**
34958          * @event arrange
34959          * Fire when arrange the file data
34960          * @param {Roo.bootstrap.UploadCropbox} this
34961          * @param {Object} formData
34962          */
34963         "arrange" : true
34964     });
34965     
34966     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34967 };
34968
34969 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34970     
34971     emptyText : 'Click to upload image',
34972     rotateNotify : 'Image is too small to rotate',
34973     errorTimeout : 3000,
34974     scale : 0,
34975     baseScale : 1,
34976     rotate : 0,
34977     dragable : false,
34978     pinching : false,
34979     mouseX : 0,
34980     mouseY : 0,
34981     cropData : false,
34982     minWidth : 300,
34983     minHeight : 300,
34984     file : false,
34985     exif : {},
34986     baseRotate : 1,
34987     cropType : 'image/jpeg',
34988     buttons : false,
34989     canvasLoaded : false,
34990     isDocument : false,
34991     method : 'POST',
34992     paramName : 'imageUpload',
34993     loadMask : true,
34994     loadingText : 'Loading...',
34995     maskEl : false,
34996     
34997     getAutoCreate : function()
34998     {
34999         var cfg = {
35000             tag : 'div',
35001             cls : 'roo-upload-cropbox',
35002             cn : [
35003                 {
35004                     tag : 'input',
35005                     cls : 'roo-upload-cropbox-selector',
35006                     type : 'file'
35007                 },
35008                 {
35009                     tag : 'div',
35010                     cls : 'roo-upload-cropbox-body',
35011                     style : 'cursor:pointer',
35012                     cn : [
35013                         {
35014                             tag : 'div',
35015                             cls : 'roo-upload-cropbox-preview'
35016                         },
35017                         {
35018                             tag : 'div',
35019                             cls : 'roo-upload-cropbox-thumb'
35020                         },
35021                         {
35022                             tag : 'div',
35023                             cls : 'roo-upload-cropbox-empty-notify',
35024                             html : this.emptyText
35025                         },
35026                         {
35027                             tag : 'div',
35028                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35029                             html : this.rotateNotify
35030                         }
35031                     ]
35032                 },
35033                 {
35034                     tag : 'div',
35035                     cls : 'roo-upload-cropbox-footer',
35036                     cn : {
35037                         tag : 'div',
35038                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35039                         cn : []
35040                     }
35041                 }
35042             ]
35043         };
35044         
35045         return cfg;
35046     },
35047     
35048     onRender : function(ct, position)
35049     {
35050         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35051         
35052         if (this.buttons.length) {
35053             
35054             Roo.each(this.buttons, function(bb) {
35055                 
35056                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35057                 
35058                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35059                 
35060             }, this);
35061         }
35062         
35063         if(this.loadMask){
35064             this.maskEl = this.el;
35065         }
35066     },
35067     
35068     initEvents : function()
35069     {
35070         this.urlAPI = (window.createObjectURL && window) || 
35071                                 (window.URL && URL.revokeObjectURL && URL) || 
35072                                 (window.webkitURL && webkitURL);
35073                         
35074         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35075         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35076         
35077         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35078         this.selectorEl.hide();
35079         
35080         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35081         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35082         
35083         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35084         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35085         this.thumbEl.hide();
35086         
35087         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35088         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35089         
35090         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35091         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35092         this.errorEl.hide();
35093         
35094         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35095         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35096         this.footerEl.hide();
35097         
35098         this.setThumbBoxSize();
35099         
35100         this.bind();
35101         
35102         this.resize();
35103         
35104         this.fireEvent('initial', this);
35105     },
35106
35107     bind : function()
35108     {
35109         var _this = this;
35110         
35111         window.addEventListener("resize", function() { _this.resize(); } );
35112         
35113         this.bodyEl.on('click', this.beforeSelectFile, this);
35114         
35115         if(Roo.isTouch){
35116             this.bodyEl.on('touchstart', this.onTouchStart, this);
35117             this.bodyEl.on('touchmove', this.onTouchMove, this);
35118             this.bodyEl.on('touchend', this.onTouchEnd, this);
35119         }
35120         
35121         if(!Roo.isTouch){
35122             this.bodyEl.on('mousedown', this.onMouseDown, this);
35123             this.bodyEl.on('mousemove', this.onMouseMove, this);
35124             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35125             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35126             Roo.get(document).on('mouseup', this.onMouseUp, this);
35127         }
35128         
35129         this.selectorEl.on('change', this.onFileSelected, this);
35130     },
35131     
35132     reset : function()
35133     {    
35134         this.scale = 0;
35135         this.baseScale = 1;
35136         this.rotate = 0;
35137         this.baseRotate = 1;
35138         this.dragable = false;
35139         this.pinching = false;
35140         this.mouseX = 0;
35141         this.mouseY = 0;
35142         this.cropData = false;
35143         this.notifyEl.dom.innerHTML = this.emptyText;
35144         
35145         this.selectorEl.dom.value = '';
35146         
35147     },
35148     
35149     resize : function()
35150     {
35151         if(this.fireEvent('resize', this) != false){
35152             this.setThumbBoxPosition();
35153             this.setCanvasPosition();
35154         }
35155     },
35156     
35157     onFooterButtonClick : function(e, el, o, type)
35158     {
35159         switch (type) {
35160             case 'rotate-left' :
35161                 this.onRotateLeft(e);
35162                 break;
35163             case 'rotate-right' :
35164                 this.onRotateRight(e);
35165                 break;
35166             case 'picture' :
35167                 this.beforeSelectFile(e);
35168                 break;
35169             case 'trash' :
35170                 this.trash(e);
35171                 break;
35172             case 'crop' :
35173                 this.crop(e);
35174                 break;
35175             case 'download' :
35176                 this.download(e);
35177                 break;
35178             default :
35179                 break;
35180         }
35181         
35182         this.fireEvent('footerbuttonclick', this, type);
35183     },
35184     
35185     beforeSelectFile : function(e)
35186     {
35187         e.preventDefault();
35188         
35189         if(this.fireEvent('beforeselectfile', this) != false){
35190             this.selectorEl.dom.click();
35191         }
35192     },
35193     
35194     onFileSelected : function(e)
35195     {
35196         e.preventDefault();
35197         
35198         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35199             return;
35200         }
35201         
35202         var file = this.selectorEl.dom.files[0];
35203         
35204         if(this.fireEvent('inspect', this, file) != false){
35205             this.prepare(file);
35206         }
35207         
35208     },
35209     
35210     trash : function(e)
35211     {
35212         this.fireEvent('trash', this);
35213     },
35214     
35215     download : function(e)
35216     {
35217         this.fireEvent('download', this);
35218     },
35219     
35220     loadCanvas : function(src)
35221     {   
35222         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35223             
35224             this.reset();
35225             
35226             this.imageEl = document.createElement('img');
35227             
35228             var _this = this;
35229             
35230             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35231             
35232             this.imageEl.src = src;
35233         }
35234     },
35235     
35236     onLoadCanvas : function()
35237     {   
35238         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35239         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35240         
35241         this.bodyEl.un('click', this.beforeSelectFile, this);
35242         
35243         this.notifyEl.hide();
35244         this.thumbEl.show();
35245         this.footerEl.show();
35246         
35247         this.baseRotateLevel();
35248         
35249         if(this.isDocument){
35250             this.setThumbBoxSize();
35251         }
35252         
35253         this.setThumbBoxPosition();
35254         
35255         this.baseScaleLevel();
35256         
35257         this.draw();
35258         
35259         this.resize();
35260         
35261         this.canvasLoaded = true;
35262         
35263         if(this.loadMask){
35264             this.maskEl.unmask();
35265         }
35266         
35267     },
35268     
35269     setCanvasPosition : function()
35270     {   
35271         if(!this.canvasEl){
35272             return;
35273         }
35274         
35275         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35276         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35277         
35278         this.previewEl.setLeft(pw);
35279         this.previewEl.setTop(ph);
35280         
35281     },
35282     
35283     onMouseDown : function(e)
35284     {   
35285         e.stopEvent();
35286         
35287         this.dragable = true;
35288         this.pinching = false;
35289         
35290         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35291             this.dragable = false;
35292             return;
35293         }
35294         
35295         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35296         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35297         
35298     },
35299     
35300     onMouseMove : function(e)
35301     {   
35302         e.stopEvent();
35303         
35304         if(!this.canvasLoaded){
35305             return;
35306         }
35307         
35308         if (!this.dragable){
35309             return;
35310         }
35311         
35312         var minX = Math.ceil(this.thumbEl.getLeft(true));
35313         var minY = Math.ceil(this.thumbEl.getTop(true));
35314         
35315         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35316         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35317         
35318         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35319         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35320         
35321         x = x - this.mouseX;
35322         y = y - this.mouseY;
35323         
35324         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35325         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35326         
35327         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35328         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35329         
35330         this.previewEl.setLeft(bgX);
35331         this.previewEl.setTop(bgY);
35332         
35333         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35334         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35335     },
35336     
35337     onMouseUp : function(e)
35338     {   
35339         e.stopEvent();
35340         
35341         this.dragable = false;
35342     },
35343     
35344     onMouseWheel : function(e)
35345     {   
35346         e.stopEvent();
35347         
35348         this.startScale = this.scale;
35349         
35350         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35351         
35352         if(!this.zoomable()){
35353             this.scale = this.startScale;
35354             return;
35355         }
35356         
35357         this.draw();
35358         
35359         return;
35360     },
35361     
35362     zoomable : function()
35363     {
35364         var minScale = this.thumbEl.getWidth() / this.minWidth;
35365         
35366         if(this.minWidth < this.minHeight){
35367             minScale = this.thumbEl.getHeight() / this.minHeight;
35368         }
35369         
35370         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35371         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35372         
35373         if(
35374                 this.isDocument &&
35375                 (this.rotate == 0 || this.rotate == 180) && 
35376                 (
35377                     width > this.imageEl.OriginWidth || 
35378                     height > this.imageEl.OriginHeight ||
35379                     (width < this.minWidth && height < this.minHeight)
35380                 )
35381         ){
35382             return false;
35383         }
35384         
35385         if(
35386                 this.isDocument &&
35387                 (this.rotate == 90 || this.rotate == 270) && 
35388                 (
35389                     width > this.imageEl.OriginWidth || 
35390                     height > this.imageEl.OriginHeight ||
35391                     (width < this.minHeight && height < this.minWidth)
35392                 )
35393         ){
35394             return false;
35395         }
35396         
35397         if(
35398                 !this.isDocument &&
35399                 (this.rotate == 0 || this.rotate == 180) && 
35400                 (
35401                     width < this.minWidth || 
35402                     width > this.imageEl.OriginWidth || 
35403                     height < this.minHeight || 
35404                     height > this.imageEl.OriginHeight
35405                 )
35406         ){
35407             return false;
35408         }
35409         
35410         if(
35411                 !this.isDocument &&
35412                 (this.rotate == 90 || this.rotate == 270) && 
35413                 (
35414                     width < this.minHeight || 
35415                     width > this.imageEl.OriginWidth || 
35416                     height < this.minWidth || 
35417                     height > this.imageEl.OriginHeight
35418                 )
35419         ){
35420             return false;
35421         }
35422         
35423         return true;
35424         
35425     },
35426     
35427     onRotateLeft : function(e)
35428     {   
35429         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35430             
35431             var minScale = this.thumbEl.getWidth() / this.minWidth;
35432             
35433             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35434             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35435             
35436             this.startScale = this.scale;
35437             
35438             while (this.getScaleLevel() < minScale){
35439             
35440                 this.scale = this.scale + 1;
35441                 
35442                 if(!this.zoomable()){
35443                     break;
35444                 }
35445                 
35446                 if(
35447                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35448                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35449                 ){
35450                     continue;
35451                 }
35452                 
35453                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35454
35455                 this.draw();
35456                 
35457                 return;
35458             }
35459             
35460             this.scale = this.startScale;
35461             
35462             this.onRotateFail();
35463             
35464             return false;
35465         }
35466         
35467         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35468
35469         if(this.isDocument){
35470             this.setThumbBoxSize();
35471             this.setThumbBoxPosition();
35472             this.setCanvasPosition();
35473         }
35474         
35475         this.draw();
35476         
35477         this.fireEvent('rotate', this, 'left');
35478         
35479     },
35480     
35481     onRotateRight : function(e)
35482     {
35483         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35484             
35485             var minScale = this.thumbEl.getWidth() / this.minWidth;
35486         
35487             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35488             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35489             
35490             this.startScale = this.scale;
35491             
35492             while (this.getScaleLevel() < minScale){
35493             
35494                 this.scale = this.scale + 1;
35495                 
35496                 if(!this.zoomable()){
35497                     break;
35498                 }
35499                 
35500                 if(
35501                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35502                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35503                 ){
35504                     continue;
35505                 }
35506                 
35507                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35508
35509                 this.draw();
35510                 
35511                 return;
35512             }
35513             
35514             this.scale = this.startScale;
35515             
35516             this.onRotateFail();
35517             
35518             return false;
35519         }
35520         
35521         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35522
35523         if(this.isDocument){
35524             this.setThumbBoxSize();
35525             this.setThumbBoxPosition();
35526             this.setCanvasPosition();
35527         }
35528         
35529         this.draw();
35530         
35531         this.fireEvent('rotate', this, 'right');
35532     },
35533     
35534     onRotateFail : function()
35535     {
35536         this.errorEl.show(true);
35537         
35538         var _this = this;
35539         
35540         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35541     },
35542     
35543     draw : function()
35544     {
35545         this.previewEl.dom.innerHTML = '';
35546         
35547         var canvasEl = document.createElement("canvas");
35548         
35549         var contextEl = canvasEl.getContext("2d");
35550         
35551         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35552         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35553         var center = this.imageEl.OriginWidth / 2;
35554         
35555         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35556             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35557             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35558             center = this.imageEl.OriginHeight / 2;
35559         }
35560         
35561         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35562         
35563         contextEl.translate(center, center);
35564         contextEl.rotate(this.rotate * Math.PI / 180);
35565
35566         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35567         
35568         this.canvasEl = document.createElement("canvas");
35569         
35570         this.contextEl = this.canvasEl.getContext("2d");
35571         
35572         switch (this.rotate) {
35573             case 0 :
35574                 
35575                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35576                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35577                 
35578                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35579                 
35580                 break;
35581             case 90 : 
35582                 
35583                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35584                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35585                 
35586                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35587                     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);
35588                     break;
35589                 }
35590                 
35591                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35592                 
35593                 break;
35594             case 180 :
35595                 
35596                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35597                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35598                 
35599                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35600                     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);
35601                     break;
35602                 }
35603                 
35604                 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);
35605                 
35606                 break;
35607             case 270 :
35608                 
35609                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35610                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35611         
35612                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35613                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35614                     break;
35615                 }
35616                 
35617                 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);
35618                 
35619                 break;
35620             default : 
35621                 break;
35622         }
35623         
35624         this.previewEl.appendChild(this.canvasEl);
35625         
35626         this.setCanvasPosition();
35627     },
35628     
35629     crop : function()
35630     {
35631         if(!this.canvasLoaded){
35632             return;
35633         }
35634         
35635         var imageCanvas = document.createElement("canvas");
35636         
35637         var imageContext = imageCanvas.getContext("2d");
35638         
35639         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35640         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35641         
35642         var center = imageCanvas.width / 2;
35643         
35644         imageContext.translate(center, center);
35645         
35646         imageContext.rotate(this.rotate * Math.PI / 180);
35647         
35648         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35649         
35650         var canvas = document.createElement("canvas");
35651         
35652         var context = canvas.getContext("2d");
35653                 
35654         canvas.width = this.minWidth;
35655         canvas.height = this.minHeight;
35656
35657         switch (this.rotate) {
35658             case 0 :
35659                 
35660                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35661                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35662                 
35663                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35664                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35665                 
35666                 var targetWidth = this.minWidth - 2 * x;
35667                 var targetHeight = this.minHeight - 2 * y;
35668                 
35669                 var scale = 1;
35670                 
35671                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35672                     scale = targetWidth / width;
35673                 }
35674                 
35675                 if(x > 0 && y == 0){
35676                     scale = targetHeight / height;
35677                 }
35678                 
35679                 if(x > 0 && y > 0){
35680                     scale = targetWidth / width;
35681                     
35682                     if(width < height){
35683                         scale = targetHeight / height;
35684                     }
35685                 }
35686                 
35687                 context.scale(scale, scale);
35688                 
35689                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35690                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35691
35692                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35693                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35694
35695                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35696                 
35697                 break;
35698             case 90 : 
35699                 
35700                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35701                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35702                 
35703                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35704                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35705                 
35706                 var targetWidth = this.minWidth - 2 * x;
35707                 var targetHeight = this.minHeight - 2 * y;
35708                 
35709                 var scale = 1;
35710                 
35711                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35712                     scale = targetWidth / width;
35713                 }
35714                 
35715                 if(x > 0 && y == 0){
35716                     scale = targetHeight / height;
35717                 }
35718                 
35719                 if(x > 0 && y > 0){
35720                     scale = targetWidth / width;
35721                     
35722                     if(width < height){
35723                         scale = targetHeight / height;
35724                     }
35725                 }
35726                 
35727                 context.scale(scale, scale);
35728                 
35729                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35730                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35731
35732                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35733                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35734                 
35735                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35736                 
35737                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35738                 
35739                 break;
35740             case 180 :
35741                 
35742                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35743                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35744                 
35745                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35746                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35747                 
35748                 var targetWidth = this.minWidth - 2 * x;
35749                 var targetHeight = this.minHeight - 2 * y;
35750                 
35751                 var scale = 1;
35752                 
35753                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35754                     scale = targetWidth / width;
35755                 }
35756                 
35757                 if(x > 0 && y == 0){
35758                     scale = targetHeight / height;
35759                 }
35760                 
35761                 if(x > 0 && y > 0){
35762                     scale = targetWidth / width;
35763                     
35764                     if(width < height){
35765                         scale = targetHeight / height;
35766                     }
35767                 }
35768                 
35769                 context.scale(scale, scale);
35770                 
35771                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35772                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35773
35774                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35775                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35776
35777                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35778                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35779                 
35780                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35781                 
35782                 break;
35783             case 270 :
35784                 
35785                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35786                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35787                 
35788                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35789                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35790                 
35791                 var targetWidth = this.minWidth - 2 * x;
35792                 var targetHeight = this.minHeight - 2 * y;
35793                 
35794                 var scale = 1;
35795                 
35796                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35797                     scale = targetWidth / width;
35798                 }
35799                 
35800                 if(x > 0 && y == 0){
35801                     scale = targetHeight / height;
35802                 }
35803                 
35804                 if(x > 0 && y > 0){
35805                     scale = targetWidth / width;
35806                     
35807                     if(width < height){
35808                         scale = targetHeight / height;
35809                     }
35810                 }
35811                 
35812                 context.scale(scale, scale);
35813                 
35814                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35815                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35816
35817                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35818                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35819                 
35820                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35821                 
35822                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35823                 
35824                 break;
35825             default : 
35826                 break;
35827         }
35828         
35829         this.cropData = canvas.toDataURL(this.cropType);
35830         
35831         if(this.fireEvent('crop', this, this.cropData) !== false){
35832             this.process(this.file, this.cropData);
35833         }
35834         
35835         return;
35836         
35837     },
35838     
35839     setThumbBoxSize : function()
35840     {
35841         var width, height;
35842         
35843         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35844             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35845             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35846             
35847             this.minWidth = width;
35848             this.minHeight = height;
35849             
35850             if(this.rotate == 90 || this.rotate == 270){
35851                 this.minWidth = height;
35852                 this.minHeight = width;
35853             }
35854         }
35855         
35856         height = 300;
35857         width = Math.ceil(this.minWidth * height / this.minHeight);
35858         
35859         if(this.minWidth > this.minHeight){
35860             width = 300;
35861             height = Math.ceil(this.minHeight * width / this.minWidth);
35862         }
35863         
35864         this.thumbEl.setStyle({
35865             width : width + 'px',
35866             height : height + 'px'
35867         });
35868
35869         return;
35870             
35871     },
35872     
35873     setThumbBoxPosition : function()
35874     {
35875         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35876         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35877         
35878         this.thumbEl.setLeft(x);
35879         this.thumbEl.setTop(y);
35880         
35881     },
35882     
35883     baseRotateLevel : function()
35884     {
35885         this.baseRotate = 1;
35886         
35887         if(
35888                 typeof(this.exif) != 'undefined' &&
35889                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35890                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35891         ){
35892             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35893         }
35894         
35895         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35896         
35897     },
35898     
35899     baseScaleLevel : function()
35900     {
35901         var width, height;
35902         
35903         if(this.isDocument){
35904             
35905             if(this.baseRotate == 6 || this.baseRotate == 8){
35906             
35907                 height = this.thumbEl.getHeight();
35908                 this.baseScale = height / this.imageEl.OriginWidth;
35909
35910                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35911                     width = this.thumbEl.getWidth();
35912                     this.baseScale = width / this.imageEl.OriginHeight;
35913                 }
35914
35915                 return;
35916             }
35917
35918             height = this.thumbEl.getHeight();
35919             this.baseScale = height / this.imageEl.OriginHeight;
35920
35921             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35922                 width = this.thumbEl.getWidth();
35923                 this.baseScale = width / this.imageEl.OriginWidth;
35924             }
35925
35926             return;
35927         }
35928         
35929         if(this.baseRotate == 6 || this.baseRotate == 8){
35930             
35931             width = this.thumbEl.getHeight();
35932             this.baseScale = width / this.imageEl.OriginHeight;
35933             
35934             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35935                 height = this.thumbEl.getWidth();
35936                 this.baseScale = height / this.imageEl.OriginHeight;
35937             }
35938             
35939             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35940                 height = this.thumbEl.getWidth();
35941                 this.baseScale = height / this.imageEl.OriginHeight;
35942                 
35943                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35944                     width = this.thumbEl.getHeight();
35945                     this.baseScale = width / this.imageEl.OriginWidth;
35946                 }
35947             }
35948             
35949             return;
35950         }
35951         
35952         width = this.thumbEl.getWidth();
35953         this.baseScale = width / this.imageEl.OriginWidth;
35954         
35955         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35956             height = this.thumbEl.getHeight();
35957             this.baseScale = height / this.imageEl.OriginHeight;
35958         }
35959         
35960         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35961             
35962             height = this.thumbEl.getHeight();
35963             this.baseScale = height / this.imageEl.OriginHeight;
35964             
35965             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35966                 width = this.thumbEl.getWidth();
35967                 this.baseScale = width / this.imageEl.OriginWidth;
35968             }
35969             
35970         }
35971         
35972         return;
35973     },
35974     
35975     getScaleLevel : function()
35976     {
35977         return this.baseScale * Math.pow(1.1, this.scale);
35978     },
35979     
35980     onTouchStart : function(e)
35981     {
35982         if(!this.canvasLoaded){
35983             this.beforeSelectFile(e);
35984             return;
35985         }
35986         
35987         var touches = e.browserEvent.touches;
35988         
35989         if(!touches){
35990             return;
35991         }
35992         
35993         if(touches.length == 1){
35994             this.onMouseDown(e);
35995             return;
35996         }
35997         
35998         if(touches.length != 2){
35999             return;
36000         }
36001         
36002         var coords = [];
36003         
36004         for(var i = 0, finger; finger = touches[i]; i++){
36005             coords.push(finger.pageX, finger.pageY);
36006         }
36007         
36008         var x = Math.pow(coords[0] - coords[2], 2);
36009         var y = Math.pow(coords[1] - coords[3], 2);
36010         
36011         this.startDistance = Math.sqrt(x + y);
36012         
36013         this.startScale = this.scale;
36014         
36015         this.pinching = true;
36016         this.dragable = false;
36017         
36018     },
36019     
36020     onTouchMove : function(e)
36021     {
36022         if(!this.pinching && !this.dragable){
36023             return;
36024         }
36025         
36026         var touches = e.browserEvent.touches;
36027         
36028         if(!touches){
36029             return;
36030         }
36031         
36032         if(this.dragable){
36033             this.onMouseMove(e);
36034             return;
36035         }
36036         
36037         var coords = [];
36038         
36039         for(var i = 0, finger; finger = touches[i]; i++){
36040             coords.push(finger.pageX, finger.pageY);
36041         }
36042         
36043         var x = Math.pow(coords[0] - coords[2], 2);
36044         var y = Math.pow(coords[1] - coords[3], 2);
36045         
36046         this.endDistance = Math.sqrt(x + y);
36047         
36048         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36049         
36050         if(!this.zoomable()){
36051             this.scale = this.startScale;
36052             return;
36053         }
36054         
36055         this.draw();
36056         
36057     },
36058     
36059     onTouchEnd : function(e)
36060     {
36061         this.pinching = false;
36062         this.dragable = false;
36063         
36064     },
36065     
36066     process : function(file, crop)
36067     {
36068         if(this.loadMask){
36069             this.maskEl.mask(this.loadingText);
36070         }
36071         
36072         this.xhr = new XMLHttpRequest();
36073         
36074         file.xhr = this.xhr;
36075
36076         this.xhr.open(this.method, this.url, true);
36077         
36078         var headers = {
36079             "Accept": "application/json",
36080             "Cache-Control": "no-cache",
36081             "X-Requested-With": "XMLHttpRequest"
36082         };
36083         
36084         for (var headerName in headers) {
36085             var headerValue = headers[headerName];
36086             if (headerValue) {
36087                 this.xhr.setRequestHeader(headerName, headerValue);
36088             }
36089         }
36090         
36091         var _this = this;
36092         
36093         this.xhr.onload = function()
36094         {
36095             _this.xhrOnLoad(_this.xhr);
36096         }
36097         
36098         this.xhr.onerror = function()
36099         {
36100             _this.xhrOnError(_this.xhr);
36101         }
36102         
36103         var formData = new FormData();
36104
36105         formData.append('returnHTML', 'NO');
36106         
36107         if(crop){
36108             formData.append('crop', crop);
36109         }
36110         
36111         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36112             formData.append(this.paramName, file, file.name);
36113         }
36114         
36115         if(typeof(file.filename) != 'undefined'){
36116             formData.append('filename', file.filename);
36117         }
36118         
36119         if(typeof(file.mimetype) != 'undefined'){
36120             formData.append('mimetype', file.mimetype);
36121         }
36122         
36123         if(this.fireEvent('arrange', this, formData) != false){
36124             this.xhr.send(formData);
36125         };
36126     },
36127     
36128     xhrOnLoad : function(xhr)
36129     {
36130         if(this.loadMask){
36131             this.maskEl.unmask();
36132         }
36133         
36134         if (xhr.readyState !== 4) {
36135             this.fireEvent('exception', this, xhr);
36136             return;
36137         }
36138
36139         var response = Roo.decode(xhr.responseText);
36140         
36141         if(!response.success){
36142             this.fireEvent('exception', this, xhr);
36143             return;
36144         }
36145         
36146         var response = Roo.decode(xhr.responseText);
36147         
36148         this.fireEvent('upload', this, response);
36149         
36150     },
36151     
36152     xhrOnError : function()
36153     {
36154         if(this.loadMask){
36155             this.maskEl.unmask();
36156         }
36157         
36158         Roo.log('xhr on error');
36159         
36160         var response = Roo.decode(xhr.responseText);
36161           
36162         Roo.log(response);
36163         
36164     },
36165     
36166     prepare : function(file)
36167     {   
36168         if(this.loadMask){
36169             this.maskEl.mask(this.loadingText);
36170         }
36171         
36172         this.file = false;
36173         this.exif = {};
36174         
36175         if(typeof(file) === 'string'){
36176             this.loadCanvas(file);
36177             return;
36178         }
36179         
36180         if(!file || !this.urlAPI){
36181             return;
36182         }
36183         
36184         this.file = file;
36185         this.cropType = file.type;
36186         
36187         var _this = this;
36188         
36189         if(this.fireEvent('prepare', this, this.file) != false){
36190             
36191             var reader = new FileReader();
36192             
36193             reader.onload = function (e) {
36194                 if (e.target.error) {
36195                     Roo.log(e.target.error);
36196                     return;
36197                 }
36198                 
36199                 var buffer = e.target.result,
36200                     dataView = new DataView(buffer),
36201                     offset = 2,
36202                     maxOffset = dataView.byteLength - 4,
36203                     markerBytes,
36204                     markerLength;
36205                 
36206                 if (dataView.getUint16(0) === 0xffd8) {
36207                     while (offset < maxOffset) {
36208                         markerBytes = dataView.getUint16(offset);
36209                         
36210                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36211                             markerLength = dataView.getUint16(offset + 2) + 2;
36212                             if (offset + markerLength > dataView.byteLength) {
36213                                 Roo.log('Invalid meta data: Invalid segment size.');
36214                                 break;
36215                             }
36216                             
36217                             if(markerBytes == 0xffe1){
36218                                 _this.parseExifData(
36219                                     dataView,
36220                                     offset,
36221                                     markerLength
36222                                 );
36223                             }
36224                             
36225                             offset += markerLength;
36226                             
36227                             continue;
36228                         }
36229                         
36230                         break;
36231                     }
36232                     
36233                 }
36234                 
36235                 var url = _this.urlAPI.createObjectURL(_this.file);
36236                 
36237                 _this.loadCanvas(url);
36238                 
36239                 return;
36240             }
36241             
36242             reader.readAsArrayBuffer(this.file);
36243             
36244         }
36245         
36246     },
36247     
36248     parseExifData : function(dataView, offset, length)
36249     {
36250         var tiffOffset = offset + 10,
36251             littleEndian,
36252             dirOffset;
36253     
36254         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36255             // No Exif data, might be XMP data instead
36256             return;
36257         }
36258         
36259         // Check for the ASCII code for "Exif" (0x45786966):
36260         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36261             // No Exif data, might be XMP data instead
36262             return;
36263         }
36264         if (tiffOffset + 8 > dataView.byteLength) {
36265             Roo.log('Invalid Exif data: Invalid segment size.');
36266             return;
36267         }
36268         // Check for the two null bytes:
36269         if (dataView.getUint16(offset + 8) !== 0x0000) {
36270             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36271             return;
36272         }
36273         // Check the byte alignment:
36274         switch (dataView.getUint16(tiffOffset)) {
36275         case 0x4949:
36276             littleEndian = true;
36277             break;
36278         case 0x4D4D:
36279             littleEndian = false;
36280             break;
36281         default:
36282             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36283             return;
36284         }
36285         // Check for the TIFF tag marker (0x002A):
36286         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36287             Roo.log('Invalid Exif data: Missing TIFF marker.');
36288             return;
36289         }
36290         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36291         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36292         
36293         this.parseExifTags(
36294             dataView,
36295             tiffOffset,
36296             tiffOffset + dirOffset,
36297             littleEndian
36298         );
36299     },
36300     
36301     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36302     {
36303         var tagsNumber,
36304             dirEndOffset,
36305             i;
36306         if (dirOffset + 6 > dataView.byteLength) {
36307             Roo.log('Invalid Exif data: Invalid directory offset.');
36308             return;
36309         }
36310         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36311         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36312         if (dirEndOffset + 4 > dataView.byteLength) {
36313             Roo.log('Invalid Exif data: Invalid directory size.');
36314             return;
36315         }
36316         for (i = 0; i < tagsNumber; i += 1) {
36317             this.parseExifTag(
36318                 dataView,
36319                 tiffOffset,
36320                 dirOffset + 2 + 12 * i, // tag offset
36321                 littleEndian
36322             );
36323         }
36324         // Return the offset to the next directory:
36325         return dataView.getUint32(dirEndOffset, littleEndian);
36326     },
36327     
36328     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36329     {
36330         var tag = dataView.getUint16(offset, littleEndian);
36331         
36332         this.exif[tag] = this.getExifValue(
36333             dataView,
36334             tiffOffset,
36335             offset,
36336             dataView.getUint16(offset + 2, littleEndian), // tag type
36337             dataView.getUint32(offset + 4, littleEndian), // tag length
36338             littleEndian
36339         );
36340     },
36341     
36342     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36343     {
36344         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36345             tagSize,
36346             dataOffset,
36347             values,
36348             i,
36349             str,
36350             c;
36351     
36352         if (!tagType) {
36353             Roo.log('Invalid Exif data: Invalid tag type.');
36354             return;
36355         }
36356         
36357         tagSize = tagType.size * length;
36358         // Determine if the value is contained in the dataOffset bytes,
36359         // or if the value at the dataOffset is a pointer to the actual data:
36360         dataOffset = tagSize > 4 ?
36361                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36362         if (dataOffset + tagSize > dataView.byteLength) {
36363             Roo.log('Invalid Exif data: Invalid data offset.');
36364             return;
36365         }
36366         if (length === 1) {
36367             return tagType.getValue(dataView, dataOffset, littleEndian);
36368         }
36369         values = [];
36370         for (i = 0; i < length; i += 1) {
36371             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36372         }
36373         
36374         if (tagType.ascii) {
36375             str = '';
36376             // Concatenate the chars:
36377             for (i = 0; i < values.length; i += 1) {
36378                 c = values[i];
36379                 // Ignore the terminating NULL byte(s):
36380                 if (c === '\u0000') {
36381                     break;
36382                 }
36383                 str += c;
36384             }
36385             return str;
36386         }
36387         return values;
36388     }
36389     
36390 });
36391
36392 Roo.apply(Roo.bootstrap.UploadCropbox, {
36393     tags : {
36394         'Orientation': 0x0112
36395     },
36396     
36397     Orientation: {
36398             1: 0, //'top-left',
36399 //            2: 'top-right',
36400             3: 180, //'bottom-right',
36401 //            4: 'bottom-left',
36402 //            5: 'left-top',
36403             6: 90, //'right-top',
36404 //            7: 'right-bottom',
36405             8: 270 //'left-bottom'
36406     },
36407     
36408     exifTagTypes : {
36409         // byte, 8-bit unsigned int:
36410         1: {
36411             getValue: function (dataView, dataOffset) {
36412                 return dataView.getUint8(dataOffset);
36413             },
36414             size: 1
36415         },
36416         // ascii, 8-bit byte:
36417         2: {
36418             getValue: function (dataView, dataOffset) {
36419                 return String.fromCharCode(dataView.getUint8(dataOffset));
36420             },
36421             size: 1,
36422             ascii: true
36423         },
36424         // short, 16 bit int:
36425         3: {
36426             getValue: function (dataView, dataOffset, littleEndian) {
36427                 return dataView.getUint16(dataOffset, littleEndian);
36428             },
36429             size: 2
36430         },
36431         // long, 32 bit int:
36432         4: {
36433             getValue: function (dataView, dataOffset, littleEndian) {
36434                 return dataView.getUint32(dataOffset, littleEndian);
36435             },
36436             size: 4
36437         },
36438         // rational = two long values, first is numerator, second is denominator:
36439         5: {
36440             getValue: function (dataView, dataOffset, littleEndian) {
36441                 return dataView.getUint32(dataOffset, littleEndian) /
36442                     dataView.getUint32(dataOffset + 4, littleEndian);
36443             },
36444             size: 8
36445         },
36446         // slong, 32 bit signed int:
36447         9: {
36448             getValue: function (dataView, dataOffset, littleEndian) {
36449                 return dataView.getInt32(dataOffset, littleEndian);
36450             },
36451             size: 4
36452         },
36453         // srational, two slongs, first is numerator, second is denominator:
36454         10: {
36455             getValue: function (dataView, dataOffset, littleEndian) {
36456                 return dataView.getInt32(dataOffset, littleEndian) /
36457                     dataView.getInt32(dataOffset + 4, littleEndian);
36458             },
36459             size: 8
36460         }
36461     },
36462     
36463     footer : {
36464         STANDARD : [
36465             {
36466                 tag : 'div',
36467                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36468                 action : 'rotate-left',
36469                 cn : [
36470                     {
36471                         tag : 'button',
36472                         cls : 'btn btn-default',
36473                         html : '<i class="fa fa-undo"></i>'
36474                     }
36475                 ]
36476             },
36477             {
36478                 tag : 'div',
36479                 cls : 'btn-group roo-upload-cropbox-picture',
36480                 action : 'picture',
36481                 cn : [
36482                     {
36483                         tag : 'button',
36484                         cls : 'btn btn-default',
36485                         html : '<i class="fa fa-picture-o"></i>'
36486                     }
36487                 ]
36488             },
36489             {
36490                 tag : 'div',
36491                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36492                 action : 'rotate-right',
36493                 cn : [
36494                     {
36495                         tag : 'button',
36496                         cls : 'btn btn-default',
36497                         html : '<i class="fa fa-repeat"></i>'
36498                     }
36499                 ]
36500             }
36501         ],
36502         DOCUMENT : [
36503             {
36504                 tag : 'div',
36505                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36506                 action : 'rotate-left',
36507                 cn : [
36508                     {
36509                         tag : 'button',
36510                         cls : 'btn btn-default',
36511                         html : '<i class="fa fa-undo"></i>'
36512                     }
36513                 ]
36514             },
36515             {
36516                 tag : 'div',
36517                 cls : 'btn-group roo-upload-cropbox-download',
36518                 action : 'download',
36519                 cn : [
36520                     {
36521                         tag : 'button',
36522                         cls : 'btn btn-default',
36523                         html : '<i class="fa fa-download"></i>'
36524                     }
36525                 ]
36526             },
36527             {
36528                 tag : 'div',
36529                 cls : 'btn-group roo-upload-cropbox-crop',
36530                 action : 'crop',
36531                 cn : [
36532                     {
36533                         tag : 'button',
36534                         cls : 'btn btn-default',
36535                         html : '<i class="fa fa-crop"></i>'
36536                     }
36537                 ]
36538             },
36539             {
36540                 tag : 'div',
36541                 cls : 'btn-group roo-upload-cropbox-trash',
36542                 action : 'trash',
36543                 cn : [
36544                     {
36545                         tag : 'button',
36546                         cls : 'btn btn-default',
36547                         html : '<i class="fa fa-trash"></i>'
36548                     }
36549                 ]
36550             },
36551             {
36552                 tag : 'div',
36553                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36554                 action : 'rotate-right',
36555                 cn : [
36556                     {
36557                         tag : 'button',
36558                         cls : 'btn btn-default',
36559                         html : '<i class="fa fa-repeat"></i>'
36560                     }
36561                 ]
36562             }
36563         ],
36564         ROTATOR : [
36565             {
36566                 tag : 'div',
36567                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36568                 action : 'rotate-left',
36569                 cn : [
36570                     {
36571                         tag : 'button',
36572                         cls : 'btn btn-default',
36573                         html : '<i class="fa fa-undo"></i>'
36574                     }
36575                 ]
36576             },
36577             {
36578                 tag : 'div',
36579                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36580                 action : 'rotate-right',
36581                 cn : [
36582                     {
36583                         tag : 'button',
36584                         cls : 'btn btn-default',
36585                         html : '<i class="fa fa-repeat"></i>'
36586                     }
36587                 ]
36588             }
36589         ]
36590     }
36591 });
36592
36593 /*
36594 * Licence: LGPL
36595 */
36596
36597 /**
36598  * @class Roo.bootstrap.DocumentManager
36599  * @extends Roo.bootstrap.Component
36600  * Bootstrap DocumentManager class
36601  * @cfg {String} paramName default 'imageUpload'
36602  * @cfg {String} toolTipName default 'filename'
36603  * @cfg {String} method default POST
36604  * @cfg {String} url action url
36605  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36606  * @cfg {Boolean} multiple multiple upload default true
36607  * @cfg {Number} thumbSize default 300
36608  * @cfg {String} fieldLabel
36609  * @cfg {Number} labelWidth default 4
36610  * @cfg {String} labelAlign (left|top) default left
36611  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36612 * @cfg {Number} labellg set the width of label (1-12)
36613  * @cfg {Number} labelmd set the width of label (1-12)
36614  * @cfg {Number} labelsm set the width of label (1-12)
36615  * @cfg {Number} labelxs set the width of label (1-12)
36616  * 
36617  * @constructor
36618  * Create a new DocumentManager
36619  * @param {Object} config The config object
36620  */
36621
36622 Roo.bootstrap.DocumentManager = function(config){
36623     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36624     
36625     this.files = [];
36626     this.delegates = [];
36627     
36628     this.addEvents({
36629         /**
36630          * @event initial
36631          * Fire when initial the DocumentManager
36632          * @param {Roo.bootstrap.DocumentManager} this
36633          */
36634         "initial" : true,
36635         /**
36636          * @event inspect
36637          * inspect selected file
36638          * @param {Roo.bootstrap.DocumentManager} this
36639          * @param {File} file
36640          */
36641         "inspect" : true,
36642         /**
36643          * @event exception
36644          * Fire when xhr load exception
36645          * @param {Roo.bootstrap.DocumentManager} this
36646          * @param {XMLHttpRequest} xhr
36647          */
36648         "exception" : true,
36649         /**
36650          * @event afterupload
36651          * Fire when xhr load exception
36652          * @param {Roo.bootstrap.DocumentManager} this
36653          * @param {XMLHttpRequest} xhr
36654          */
36655         "afterupload" : true,
36656         /**
36657          * @event prepare
36658          * prepare the form data
36659          * @param {Roo.bootstrap.DocumentManager} this
36660          * @param {Object} formData
36661          */
36662         "prepare" : true,
36663         /**
36664          * @event remove
36665          * Fire when remove the file
36666          * @param {Roo.bootstrap.DocumentManager} this
36667          * @param {Object} file
36668          */
36669         "remove" : true,
36670         /**
36671          * @event refresh
36672          * Fire after refresh the file
36673          * @param {Roo.bootstrap.DocumentManager} this
36674          */
36675         "refresh" : true,
36676         /**
36677          * @event click
36678          * Fire after click the image
36679          * @param {Roo.bootstrap.DocumentManager} this
36680          * @param {Object} file
36681          */
36682         "click" : true,
36683         /**
36684          * @event edit
36685          * Fire when upload a image and editable set to true
36686          * @param {Roo.bootstrap.DocumentManager} this
36687          * @param {Object} file
36688          */
36689         "edit" : true,
36690         /**
36691          * @event beforeselectfile
36692          * Fire before select file
36693          * @param {Roo.bootstrap.DocumentManager} this
36694          */
36695         "beforeselectfile" : true,
36696         /**
36697          * @event process
36698          * Fire before process file
36699          * @param {Roo.bootstrap.DocumentManager} this
36700          * @param {Object} file
36701          */
36702         "process" : true,
36703         /**
36704          * @event previewrendered
36705          * Fire when preview rendered
36706          * @param {Roo.bootstrap.DocumentManager} this
36707          * @param {Object} file
36708          */
36709         "previewrendered" : true,
36710         /**
36711          */
36712         "previewResize" : true
36713         
36714     });
36715 };
36716
36717 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36718     
36719     boxes : 0,
36720     inputName : '',
36721     thumbSize : 300,
36722     multiple : true,
36723     files : false,
36724     method : 'POST',
36725     url : '',
36726     paramName : 'imageUpload',
36727     toolTipName : 'filename',
36728     fieldLabel : '',
36729     labelWidth : 4,
36730     labelAlign : 'left',
36731     editable : true,
36732     delegates : false,
36733     xhr : false, 
36734     
36735     labellg : 0,
36736     labelmd : 0,
36737     labelsm : 0,
36738     labelxs : 0,
36739     
36740     getAutoCreate : function()
36741     {   
36742         var managerWidget = {
36743             tag : 'div',
36744             cls : 'roo-document-manager',
36745             cn : [
36746                 {
36747                     tag : 'input',
36748                     cls : 'roo-document-manager-selector',
36749                     type : 'file'
36750                 },
36751                 {
36752                     tag : 'div',
36753                     cls : 'roo-document-manager-uploader',
36754                     cn : [
36755                         {
36756                             tag : 'div',
36757                             cls : 'roo-document-manager-upload-btn',
36758                             html : '<i class="fa fa-plus"></i>'
36759                         }
36760                     ]
36761                     
36762                 }
36763             ]
36764         };
36765         
36766         var content = [
36767             {
36768                 tag : 'div',
36769                 cls : 'column col-md-12',
36770                 cn : managerWidget
36771             }
36772         ];
36773         
36774         if(this.fieldLabel.length){
36775             
36776             content = [
36777                 {
36778                     tag : 'div',
36779                     cls : 'column col-md-12',
36780                     html : this.fieldLabel
36781                 },
36782                 {
36783                     tag : 'div',
36784                     cls : 'column col-md-12',
36785                     cn : managerWidget
36786                 }
36787             ];
36788
36789             if(this.labelAlign == 'left'){
36790                 content = [
36791                     {
36792                         tag : 'div',
36793                         cls : 'column',
36794                         html : this.fieldLabel
36795                     },
36796                     {
36797                         tag : 'div',
36798                         cls : 'column',
36799                         cn : managerWidget
36800                     }
36801                 ];
36802                 
36803                 if(this.labelWidth > 12){
36804                     content[0].style = "width: " + this.labelWidth + 'px';
36805                 }
36806
36807                 if(this.labelWidth < 13 && this.labelmd == 0){
36808                     this.labelmd = this.labelWidth;
36809                 }
36810
36811                 if(this.labellg > 0){
36812                     content[0].cls += ' col-lg-' + this.labellg;
36813                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36814                 }
36815
36816                 if(this.labelmd > 0){
36817                     content[0].cls += ' col-md-' + this.labelmd;
36818                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36819                 }
36820
36821                 if(this.labelsm > 0){
36822                     content[0].cls += ' col-sm-' + this.labelsm;
36823                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36824                 }
36825
36826                 if(this.labelxs > 0){
36827                     content[0].cls += ' col-xs-' + this.labelxs;
36828                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36829                 }
36830                 
36831             }
36832         }
36833         
36834         var cfg = {
36835             tag : 'div',
36836             cls : 'row clearfix',
36837             cn : content
36838         };
36839         
36840         return cfg;
36841         
36842     },
36843     
36844     initEvents : function()
36845     {
36846         this.managerEl = this.el.select('.roo-document-manager', true).first();
36847         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36848         
36849         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36850         this.selectorEl.hide();
36851         
36852         if(this.multiple){
36853             this.selectorEl.attr('multiple', 'multiple');
36854         }
36855         
36856         this.selectorEl.on('change', this.onFileSelected, this);
36857         
36858         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36859         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36860         
36861         this.uploader.on('click', this.onUploaderClick, this);
36862         
36863         this.renderProgressDialog();
36864         
36865         var _this = this;
36866         
36867         window.addEventListener("resize", function() { _this.refresh(); } );
36868         
36869         this.fireEvent('initial', this);
36870     },
36871     
36872     renderProgressDialog : function()
36873     {
36874         var _this = this;
36875         
36876         this.progressDialog = new Roo.bootstrap.Modal({
36877             cls : 'roo-document-manager-progress-dialog',
36878             allow_close : false,
36879             animate : false,
36880             title : '',
36881             buttons : [
36882                 {
36883                     name  :'cancel',
36884                     weight : 'danger',
36885                     html : 'Cancel'
36886                 }
36887             ], 
36888             listeners : { 
36889                 btnclick : function() {
36890                     _this.uploadCancel();
36891                     this.hide();
36892                 }
36893             }
36894         });
36895          
36896         this.progressDialog.render(Roo.get(document.body));
36897          
36898         this.progress = new Roo.bootstrap.Progress({
36899             cls : 'roo-document-manager-progress',
36900             active : true,
36901             striped : true
36902         });
36903         
36904         this.progress.render(this.progressDialog.getChildContainer());
36905         
36906         this.progressBar = new Roo.bootstrap.ProgressBar({
36907             cls : 'roo-document-manager-progress-bar',
36908             aria_valuenow : 0,
36909             aria_valuemin : 0,
36910             aria_valuemax : 12,
36911             panel : 'success'
36912         });
36913         
36914         this.progressBar.render(this.progress.getChildContainer());
36915     },
36916     
36917     onUploaderClick : function(e)
36918     {
36919         e.preventDefault();
36920      
36921         if(this.fireEvent('beforeselectfile', this) != false){
36922             this.selectorEl.dom.click();
36923         }
36924         
36925     },
36926     
36927     onFileSelected : function(e)
36928     {
36929         e.preventDefault();
36930         
36931         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36932             return;
36933         }
36934         
36935         Roo.each(this.selectorEl.dom.files, function(file){
36936             if(this.fireEvent('inspect', this, file) != false){
36937                 this.files.push(file);
36938             }
36939         }, this);
36940         
36941         this.queue();
36942         
36943     },
36944     
36945     queue : function()
36946     {
36947         this.selectorEl.dom.value = '';
36948         
36949         if(!this.files || !this.files.length){
36950             return;
36951         }
36952         
36953         if(this.boxes > 0 && this.files.length > this.boxes){
36954             this.files = this.files.slice(0, this.boxes);
36955         }
36956         
36957         this.uploader.show();
36958         
36959         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36960             this.uploader.hide();
36961         }
36962         
36963         var _this = this;
36964         
36965         var files = [];
36966         
36967         var docs = [];
36968         
36969         Roo.each(this.files, function(file){
36970             
36971             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36972                 var f = this.renderPreview(file);
36973                 files.push(f);
36974                 return;
36975             }
36976             
36977             if(file.type.indexOf('image') != -1){
36978                 this.delegates.push(
36979                     (function(){
36980                         _this.process(file);
36981                     }).createDelegate(this)
36982                 );
36983         
36984                 return;
36985             }
36986             
36987             docs.push(
36988                 (function(){
36989                     _this.process(file);
36990                 }).createDelegate(this)
36991             );
36992             
36993         }, this);
36994         
36995         this.files = files;
36996         
36997         this.delegates = this.delegates.concat(docs);
36998         
36999         if(!this.delegates.length){
37000             this.refresh();
37001             return;
37002         }
37003         
37004         this.progressBar.aria_valuemax = this.delegates.length;
37005         
37006         this.arrange();
37007         
37008         return;
37009     },
37010     
37011     arrange : function()
37012     {
37013         if(!this.delegates.length){
37014             this.progressDialog.hide();
37015             this.refresh();
37016             return;
37017         }
37018         
37019         var delegate = this.delegates.shift();
37020         
37021         this.progressDialog.show();
37022         
37023         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37024         
37025         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37026         
37027         delegate();
37028     },
37029     
37030     refresh : function()
37031     {
37032         this.uploader.show();
37033         
37034         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37035             this.uploader.hide();
37036         }
37037         
37038         Roo.isTouch ? this.closable(false) : this.closable(true);
37039         
37040         this.fireEvent('refresh', this);
37041     },
37042     
37043     onRemove : function(e, el, o)
37044     {
37045         e.preventDefault();
37046         
37047         this.fireEvent('remove', this, o);
37048         
37049     },
37050     
37051     remove : function(o)
37052     {
37053         var files = [];
37054         
37055         Roo.each(this.files, function(file){
37056             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37057                 files.push(file);
37058                 return;
37059             }
37060
37061             o.target.remove();
37062
37063         }, this);
37064         
37065         this.files = files;
37066         
37067         this.refresh();
37068     },
37069     
37070     clear : function()
37071     {
37072         Roo.each(this.files, function(file){
37073             if(!file.target){
37074                 return;
37075             }
37076             
37077             file.target.remove();
37078
37079         }, this);
37080         
37081         this.files = [];
37082         
37083         this.refresh();
37084     },
37085     
37086     onClick : function(e, el, o)
37087     {
37088         e.preventDefault();
37089         
37090         this.fireEvent('click', this, o);
37091         
37092     },
37093     
37094     closable : function(closable)
37095     {
37096         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37097             
37098             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37099             
37100             if(closable){
37101                 el.show();
37102                 return;
37103             }
37104             
37105             el.hide();
37106             
37107         }, this);
37108     },
37109     
37110     xhrOnLoad : function(xhr)
37111     {
37112         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37113             el.remove();
37114         }, this);
37115         
37116         if (xhr.readyState !== 4) {
37117             this.arrange();
37118             this.fireEvent('exception', this, xhr);
37119             return;
37120         }
37121
37122         var response = Roo.decode(xhr.responseText);
37123         
37124         if(!response.success){
37125             this.arrange();
37126             this.fireEvent('exception', this, xhr);
37127             return;
37128         }
37129         
37130         var file = this.renderPreview(response.data);
37131         
37132         this.files.push(file);
37133         
37134         this.arrange();
37135         
37136         this.fireEvent('afterupload', this, xhr);
37137         
37138     },
37139     
37140     xhrOnError : function(xhr)
37141     {
37142         Roo.log('xhr on error');
37143         
37144         var response = Roo.decode(xhr.responseText);
37145           
37146         Roo.log(response);
37147         
37148         this.arrange();
37149     },
37150     
37151     process : function(file)
37152     {
37153         if(this.fireEvent('process', this, file) !== false){
37154             if(this.editable && file.type.indexOf('image') != -1){
37155                 this.fireEvent('edit', this, file);
37156                 return;
37157             }
37158
37159             this.uploadStart(file, false);
37160
37161             return;
37162         }
37163         
37164     },
37165     
37166     uploadStart : function(file, crop)
37167     {
37168         this.xhr = new XMLHttpRequest();
37169         
37170         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37171             this.arrange();
37172             return;
37173         }
37174         
37175         file.xhr = this.xhr;
37176             
37177         this.managerEl.createChild({
37178             tag : 'div',
37179             cls : 'roo-document-manager-loading',
37180             cn : [
37181                 {
37182                     tag : 'div',
37183                     tooltip : file.name,
37184                     cls : 'roo-document-manager-thumb',
37185                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37186                 }
37187             ]
37188
37189         });
37190
37191         this.xhr.open(this.method, this.url, true);
37192         
37193         var headers = {
37194             "Accept": "application/json",
37195             "Cache-Control": "no-cache",
37196             "X-Requested-With": "XMLHttpRequest"
37197         };
37198         
37199         for (var headerName in headers) {
37200             var headerValue = headers[headerName];
37201             if (headerValue) {
37202                 this.xhr.setRequestHeader(headerName, headerValue);
37203             }
37204         }
37205         
37206         var _this = this;
37207         
37208         this.xhr.onload = function()
37209         {
37210             _this.xhrOnLoad(_this.xhr);
37211         }
37212         
37213         this.xhr.onerror = function()
37214         {
37215             _this.xhrOnError(_this.xhr);
37216         }
37217         
37218         var formData = new FormData();
37219
37220         formData.append('returnHTML', 'NO');
37221         
37222         if(crop){
37223             formData.append('crop', crop);
37224         }
37225         
37226         formData.append(this.paramName, file, file.name);
37227         
37228         var options = {
37229             file : file, 
37230             manually : false
37231         };
37232         
37233         if(this.fireEvent('prepare', this, formData, options) != false){
37234             
37235             if(options.manually){
37236                 return;
37237             }
37238             
37239             this.xhr.send(formData);
37240             return;
37241         };
37242         
37243         this.uploadCancel();
37244     },
37245     
37246     uploadCancel : function()
37247     {
37248         if (this.xhr) {
37249             this.xhr.abort();
37250         }
37251         
37252         this.delegates = [];
37253         
37254         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37255             el.remove();
37256         }, this);
37257         
37258         this.arrange();
37259     },
37260     
37261     renderPreview : function(file)
37262     {
37263         if(typeof(file.target) != 'undefined' && file.target){
37264             return file;
37265         }
37266         
37267         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37268         
37269         var previewEl = this.managerEl.createChild({
37270             tag : 'div',
37271             cls : 'roo-document-manager-preview',
37272             cn : [
37273                 {
37274                     tag : 'div',
37275                     tooltip : file[this.toolTipName],
37276                     cls : 'roo-document-manager-thumb',
37277                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37278                 },
37279                 {
37280                     tag : 'button',
37281                     cls : 'close',
37282                     html : '<i class="fa fa-times-circle"></i>'
37283                 }
37284             ]
37285         });
37286
37287         var close = previewEl.select('button.close', true).first();
37288
37289         close.on('click', this.onRemove, this, file);
37290
37291         file.target = previewEl;
37292
37293         var image = previewEl.select('img', true).first();
37294         
37295         var _this = this;
37296         
37297         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37298         
37299         image.on('click', this.onClick, this, file);
37300         
37301         this.fireEvent('previewrendered', this, file);
37302         
37303         return file;
37304         
37305     },
37306     
37307     onPreviewLoad : function(file, image)
37308     {
37309         if(typeof(file.target) == 'undefined' || !file.target){
37310             return;
37311         }
37312         
37313         var width = image.dom.naturalWidth || image.dom.width;
37314         var height = image.dom.naturalHeight || image.dom.height;
37315         
37316         if(!this.previewResize) {
37317             return;
37318         }
37319         
37320         if(width > height){
37321             file.target.addClass('wide');
37322             return;
37323         }
37324         
37325         file.target.addClass('tall');
37326         return;
37327         
37328     },
37329     
37330     uploadFromSource : function(file, crop)
37331     {
37332         this.xhr = new XMLHttpRequest();
37333         
37334         this.managerEl.createChild({
37335             tag : 'div',
37336             cls : 'roo-document-manager-loading',
37337             cn : [
37338                 {
37339                     tag : 'div',
37340                     tooltip : file.name,
37341                     cls : 'roo-document-manager-thumb',
37342                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37343                 }
37344             ]
37345
37346         });
37347
37348         this.xhr.open(this.method, this.url, true);
37349         
37350         var headers = {
37351             "Accept": "application/json",
37352             "Cache-Control": "no-cache",
37353             "X-Requested-With": "XMLHttpRequest"
37354         };
37355         
37356         for (var headerName in headers) {
37357             var headerValue = headers[headerName];
37358             if (headerValue) {
37359                 this.xhr.setRequestHeader(headerName, headerValue);
37360             }
37361         }
37362         
37363         var _this = this;
37364         
37365         this.xhr.onload = function()
37366         {
37367             _this.xhrOnLoad(_this.xhr);
37368         }
37369         
37370         this.xhr.onerror = function()
37371         {
37372             _this.xhrOnError(_this.xhr);
37373         }
37374         
37375         var formData = new FormData();
37376
37377         formData.append('returnHTML', 'NO');
37378         
37379         formData.append('crop', crop);
37380         
37381         if(typeof(file.filename) != 'undefined'){
37382             formData.append('filename', file.filename);
37383         }
37384         
37385         if(typeof(file.mimetype) != 'undefined'){
37386             formData.append('mimetype', file.mimetype);
37387         }
37388         
37389         Roo.log(formData);
37390         
37391         if(this.fireEvent('prepare', this, formData) != false){
37392             this.xhr.send(formData);
37393         };
37394     }
37395 });
37396
37397 /*
37398 * Licence: LGPL
37399 */
37400
37401 /**
37402  * @class Roo.bootstrap.DocumentViewer
37403  * @extends Roo.bootstrap.Component
37404  * Bootstrap DocumentViewer class
37405  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37406  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37407  * 
37408  * @constructor
37409  * Create a new DocumentViewer
37410  * @param {Object} config The config object
37411  */
37412
37413 Roo.bootstrap.DocumentViewer = function(config){
37414     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37415     
37416     this.addEvents({
37417         /**
37418          * @event initial
37419          * Fire after initEvent
37420          * @param {Roo.bootstrap.DocumentViewer} this
37421          */
37422         "initial" : true,
37423         /**
37424          * @event click
37425          * Fire after click
37426          * @param {Roo.bootstrap.DocumentViewer} this
37427          */
37428         "click" : true,
37429         /**
37430          * @event download
37431          * Fire after download button
37432          * @param {Roo.bootstrap.DocumentViewer} this
37433          */
37434         "download" : true,
37435         /**
37436          * @event trash
37437          * Fire after trash button
37438          * @param {Roo.bootstrap.DocumentViewer} this
37439          */
37440         "trash" : true
37441         
37442     });
37443 };
37444
37445 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37446     
37447     showDownload : true,
37448     
37449     showTrash : true,
37450     
37451     getAutoCreate : function()
37452     {
37453         var cfg = {
37454             tag : 'div',
37455             cls : 'roo-document-viewer',
37456             cn : [
37457                 {
37458                     tag : 'div',
37459                     cls : 'roo-document-viewer-body',
37460                     cn : [
37461                         {
37462                             tag : 'div',
37463                             cls : 'roo-document-viewer-thumb',
37464                             cn : [
37465                                 {
37466                                     tag : 'img',
37467                                     cls : 'roo-document-viewer-image'
37468                                 }
37469                             ]
37470                         }
37471                     ]
37472                 },
37473                 {
37474                     tag : 'div',
37475                     cls : 'roo-document-viewer-footer',
37476                     cn : {
37477                         tag : 'div',
37478                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37479                         cn : [
37480                             {
37481                                 tag : 'div',
37482                                 cls : 'btn-group roo-document-viewer-download',
37483                                 cn : [
37484                                     {
37485                                         tag : 'button',
37486                                         cls : 'btn btn-default',
37487                                         html : '<i class="fa fa-download"></i>'
37488                                     }
37489                                 ]
37490                             },
37491                             {
37492                                 tag : 'div',
37493                                 cls : 'btn-group roo-document-viewer-trash',
37494                                 cn : [
37495                                     {
37496                                         tag : 'button',
37497                                         cls : 'btn btn-default',
37498                                         html : '<i class="fa fa-trash"></i>'
37499                                     }
37500                                 ]
37501                             }
37502                         ]
37503                     }
37504                 }
37505             ]
37506         };
37507         
37508         return cfg;
37509     },
37510     
37511     initEvents : function()
37512     {
37513         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37514         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37515         
37516         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37517         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37518         
37519         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37520         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37521         
37522         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37523         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37524         
37525         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37526         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37527         
37528         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37529         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37530         
37531         this.bodyEl.on('click', this.onClick, this);
37532         this.downloadBtn.on('click', this.onDownload, this);
37533         this.trashBtn.on('click', this.onTrash, this);
37534         
37535         this.downloadBtn.hide();
37536         this.trashBtn.hide();
37537         
37538         if(this.showDownload){
37539             this.downloadBtn.show();
37540         }
37541         
37542         if(this.showTrash){
37543             this.trashBtn.show();
37544         }
37545         
37546         if(!this.showDownload && !this.showTrash) {
37547             this.footerEl.hide();
37548         }
37549         
37550     },
37551     
37552     initial : function()
37553     {
37554         this.fireEvent('initial', this);
37555         
37556     },
37557     
37558     onClick : function(e)
37559     {
37560         e.preventDefault();
37561         
37562         this.fireEvent('click', this);
37563     },
37564     
37565     onDownload : function(e)
37566     {
37567         e.preventDefault();
37568         
37569         this.fireEvent('download', this);
37570     },
37571     
37572     onTrash : function(e)
37573     {
37574         e.preventDefault();
37575         
37576         this.fireEvent('trash', this);
37577     }
37578     
37579 });
37580 /*
37581  * - LGPL
37582  *
37583  * FieldLabel
37584  * 
37585  */
37586
37587 /**
37588  * @class Roo.bootstrap.form.FieldLabel
37589  * @extends Roo.bootstrap.Component
37590  * Bootstrap FieldLabel class
37591  * @cfg {String} html contents of the element
37592  * @cfg {String} tag tag of the element default label
37593  * @cfg {String} cls class of the element
37594  * @cfg {String} target label target 
37595  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37596  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37597  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37598  * @cfg {String} iconTooltip default "This field is required"
37599  * @cfg {String} indicatorpos (left|right) default left
37600  * 
37601  * @constructor
37602  * Create a new FieldLabel
37603  * @param {Object} config The config object
37604  */
37605
37606 Roo.bootstrap.form.FieldLabel = function(config){
37607     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37608     
37609     this.addEvents({
37610             /**
37611              * @event invalid
37612              * Fires after the field has been marked as invalid.
37613              * @param {Roo.form.FieldLabel} this
37614              * @param {String} msg The validation message
37615              */
37616             invalid : true,
37617             /**
37618              * @event valid
37619              * Fires after the field has been validated with no errors.
37620              * @param {Roo.form.FieldLabel} this
37621              */
37622             valid : true
37623         });
37624 };
37625
37626 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37627     
37628     tag: 'label',
37629     cls: '',
37630     html: '',
37631     target: '',
37632     allowBlank : true,
37633     invalidClass : 'has-warning',
37634     validClass : 'has-success',
37635     iconTooltip : 'This field is required',
37636     indicatorpos : 'left',
37637     
37638     getAutoCreate : function(){
37639         
37640         var cls = "";
37641         if (!this.allowBlank) {
37642             cls  = "visible";
37643         }
37644         
37645         var cfg = {
37646             tag : this.tag,
37647             cls : 'roo-bootstrap-field-label ' + this.cls,
37648             for : this.target,
37649             cn : [
37650                 {
37651                     tag : 'i',
37652                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37653                     tooltip : this.iconTooltip
37654                 },
37655                 {
37656                     tag : 'span',
37657                     html : this.html
37658                 }
37659             ] 
37660         };
37661         
37662         if(this.indicatorpos == 'right'){
37663             var cfg = {
37664                 tag : this.tag,
37665                 cls : 'roo-bootstrap-field-label ' + this.cls,
37666                 for : this.target,
37667                 cn : [
37668                     {
37669                         tag : 'span',
37670                         html : this.html
37671                     },
37672                     {
37673                         tag : 'i',
37674                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37675                         tooltip : this.iconTooltip
37676                     }
37677                 ] 
37678             };
37679         }
37680         
37681         return cfg;
37682     },
37683     
37684     initEvents: function() 
37685     {
37686         Roo.bootstrap.Element.superclass.initEvents.call(this);
37687         
37688         this.indicator = this.indicatorEl();
37689         
37690         if(this.indicator){
37691             this.indicator.removeClass('visible');
37692             this.indicator.addClass('invisible');
37693         }
37694         
37695         Roo.bootstrap.form.FieldLabel.register(this);
37696     },
37697     
37698     indicatorEl : function()
37699     {
37700         var indicator = this.el.select('i.roo-required-indicator',true).first();
37701         
37702         if(!indicator){
37703             return false;
37704         }
37705         
37706         return indicator;
37707         
37708     },
37709     
37710     /**
37711      * Mark this field as valid
37712      */
37713     markValid : function()
37714     {
37715         if(this.indicator){
37716             this.indicator.removeClass('visible');
37717             this.indicator.addClass('invisible');
37718         }
37719         if (Roo.bootstrap.version == 3) {
37720             this.el.removeClass(this.invalidClass);
37721             this.el.addClass(this.validClass);
37722         } else {
37723             this.el.removeClass('is-invalid');
37724             this.el.addClass('is-valid');
37725         }
37726         
37727         
37728         this.fireEvent('valid', this);
37729     },
37730     
37731     /**
37732      * Mark this field as invalid
37733      * @param {String} msg The validation message
37734      */
37735     markInvalid : function(msg)
37736     {
37737         if(this.indicator){
37738             this.indicator.removeClass('invisible');
37739             this.indicator.addClass('visible');
37740         }
37741           if (Roo.bootstrap.version == 3) {
37742             this.el.removeClass(this.validClass);
37743             this.el.addClass(this.invalidClass);
37744         } else {
37745             this.el.removeClass('is-valid');
37746             this.el.addClass('is-invalid');
37747         }
37748         
37749         
37750         this.fireEvent('invalid', this, msg);
37751     }
37752     
37753    
37754 });
37755
37756 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37757     
37758     groups: {},
37759     
37760      /**
37761     * register a FieldLabel Group
37762     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37763     */
37764     register : function(label)
37765     {
37766         if(this.groups.hasOwnProperty(label.target)){
37767             return;
37768         }
37769      
37770         this.groups[label.target] = label;
37771         
37772     },
37773     /**
37774     * fetch a FieldLabel Group based on the target
37775     * @param {string} target
37776     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37777     */
37778     get: function(target) {
37779         if (typeof(this.groups[target]) == 'undefined') {
37780             return false;
37781         }
37782         
37783         return this.groups[target] ;
37784     }
37785 });
37786
37787  
37788
37789  /*
37790  * - LGPL
37791  *
37792  * page DateSplitField.
37793  * 
37794  */
37795
37796
37797 /**
37798  * @class Roo.bootstrap.form.DateSplitField
37799  * @extends Roo.bootstrap.Component
37800  * Bootstrap DateSplitField class
37801  * @cfg {string} fieldLabel - the label associated
37802  * @cfg {Number} labelWidth set the width of label (0-12)
37803  * @cfg {String} labelAlign (top|left)
37804  * @cfg {Boolean} dayAllowBlank (true|false) default false
37805  * @cfg {Boolean} monthAllowBlank (true|false) default false
37806  * @cfg {Boolean} yearAllowBlank (true|false) default false
37807  * @cfg {string} dayPlaceholder 
37808  * @cfg {string} monthPlaceholder
37809  * @cfg {string} yearPlaceholder
37810  * @cfg {string} dayFormat default 'd'
37811  * @cfg {string} monthFormat default 'm'
37812  * @cfg {string} yearFormat default 'Y'
37813  * @cfg {Number} labellg set the width of label (1-12)
37814  * @cfg {Number} labelmd set the width of label (1-12)
37815  * @cfg {Number} labelsm set the width of label (1-12)
37816  * @cfg {Number} labelxs set the width of label (1-12)
37817
37818  *     
37819  * @constructor
37820  * Create a new DateSplitField
37821  * @param {Object} config The config object
37822  */
37823
37824 Roo.bootstrap.form.DateSplitField = function(config){
37825     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37826     
37827     this.addEvents({
37828         // raw events
37829          /**
37830          * @event years
37831          * getting the data of years
37832          * @param {Roo.bootstrap.form.DateSplitField} this
37833          * @param {Object} years
37834          */
37835         "years" : true,
37836         /**
37837          * @event days
37838          * getting the data of days
37839          * @param {Roo.bootstrap.form.DateSplitField} this
37840          * @param {Object} days
37841          */
37842         "days" : true,
37843         /**
37844          * @event invalid
37845          * Fires after the field has been marked as invalid.
37846          * @param {Roo.form.Field} this
37847          * @param {String} msg The validation message
37848          */
37849         invalid : true,
37850        /**
37851          * @event valid
37852          * Fires after the field has been validated with no errors.
37853          * @param {Roo.form.Field} this
37854          */
37855         valid : true
37856     });
37857 };
37858
37859 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37860     
37861     fieldLabel : '',
37862     labelAlign : 'top',
37863     labelWidth : 3,
37864     dayAllowBlank : false,
37865     monthAllowBlank : false,
37866     yearAllowBlank : false,
37867     dayPlaceholder : '',
37868     monthPlaceholder : '',
37869     yearPlaceholder : '',
37870     dayFormat : 'd',
37871     monthFormat : 'm',
37872     yearFormat : 'Y',
37873     isFormField : true,
37874     labellg : 0,
37875     labelmd : 0,
37876     labelsm : 0,
37877     labelxs : 0,
37878     
37879     getAutoCreate : function()
37880     {
37881         var cfg = {
37882             tag : 'div',
37883             cls : 'row roo-date-split-field-group',
37884             cn : [
37885                 {
37886                     tag : 'input',
37887                     type : 'hidden',
37888                     cls : 'form-hidden-field roo-date-split-field-group-value',
37889                     name : this.name
37890                 }
37891             ]
37892         };
37893         
37894         var labelCls = 'col-md-12';
37895         var contentCls = 'col-md-4';
37896         
37897         if(this.fieldLabel){
37898             
37899             var label = {
37900                 tag : 'div',
37901                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37902                 cn : [
37903                     {
37904                         tag : 'label',
37905                         html : this.fieldLabel
37906                     }
37907                 ]
37908             };
37909             
37910             if(this.labelAlign == 'left'){
37911             
37912                 if(this.labelWidth > 12){
37913                     label.style = "width: " + this.labelWidth + 'px';
37914                 }
37915
37916                 if(this.labelWidth < 13 && this.labelmd == 0){
37917                     this.labelmd = this.labelWidth;
37918                 }
37919
37920                 if(this.labellg > 0){
37921                     labelCls = ' col-lg-' + this.labellg;
37922                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37923                 }
37924
37925                 if(this.labelmd > 0){
37926                     labelCls = ' col-md-' + this.labelmd;
37927                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37928                 }
37929
37930                 if(this.labelsm > 0){
37931                     labelCls = ' col-sm-' + this.labelsm;
37932                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37933                 }
37934
37935                 if(this.labelxs > 0){
37936                     labelCls = ' col-xs-' + this.labelxs;
37937                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37938                 }
37939             }
37940             
37941             label.cls += ' ' + labelCls;
37942             
37943             cfg.cn.push(label);
37944         }
37945         
37946         Roo.each(['day', 'month', 'year'], function(t){
37947             cfg.cn.push({
37948                 tag : 'div',
37949                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37950             });
37951         }, this);
37952         
37953         return cfg;
37954     },
37955     
37956     inputEl: function ()
37957     {
37958         return this.el.select('.roo-date-split-field-group-value', true).first();
37959     },
37960     
37961     onRender : function(ct, position) 
37962     {
37963         var _this = this;
37964         
37965         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37966         
37967         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37968         
37969         this.dayField = new Roo.bootstrap.form.ComboBox({
37970             allowBlank : this.dayAllowBlank,
37971             alwaysQuery : true,
37972             displayField : 'value',
37973             editable : false,
37974             fieldLabel : '',
37975             forceSelection : true,
37976             mode : 'local',
37977             placeholder : this.dayPlaceholder,
37978             selectOnFocus : true,
37979             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37980             triggerAction : 'all',
37981             typeAhead : true,
37982             valueField : 'value',
37983             store : new Roo.data.SimpleStore({
37984                 data : (function() {    
37985                     var days = [];
37986                     _this.fireEvent('days', _this, days);
37987                     return days;
37988                 })(),
37989                 fields : [ 'value' ]
37990             }),
37991             listeners : {
37992                 select : function (_self, record, index)
37993                 {
37994                     _this.setValue(_this.getValue());
37995                 }
37996             }
37997         });
37998
37999         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38000         
38001         this.monthField = new Roo.bootstrap.form.MonthField({
38002             after : '<i class=\"fa fa-calendar\"></i>',
38003             allowBlank : this.monthAllowBlank,
38004             placeholder : this.monthPlaceholder,
38005             readOnly : true,
38006             listeners : {
38007                 render : function (_self)
38008                 {
38009                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38010                         e.preventDefault();
38011                         _self.focus();
38012                     });
38013                 },
38014                 select : function (_self, oldvalue, newvalue)
38015                 {
38016                     _this.setValue(_this.getValue());
38017                 }
38018             }
38019         });
38020         
38021         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38022         
38023         this.yearField = new Roo.bootstrap.form.ComboBox({
38024             allowBlank : this.yearAllowBlank,
38025             alwaysQuery : true,
38026             displayField : 'value',
38027             editable : false,
38028             fieldLabel : '',
38029             forceSelection : true,
38030             mode : 'local',
38031             placeholder : this.yearPlaceholder,
38032             selectOnFocus : true,
38033             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38034             triggerAction : 'all',
38035             typeAhead : true,
38036             valueField : 'value',
38037             store : new Roo.data.SimpleStore({
38038                 data : (function() {
38039                     var years = [];
38040                     _this.fireEvent('years', _this, years);
38041                     return years;
38042                 })(),
38043                 fields : [ 'value' ]
38044             }),
38045             listeners : {
38046                 select : function (_self, record, index)
38047                 {
38048                     _this.setValue(_this.getValue());
38049                 }
38050             }
38051         });
38052
38053         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38054     },
38055     
38056     setValue : function(v, format)
38057     {
38058         this.inputEl.dom.value = v;
38059         
38060         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38061         
38062         var d = Date.parseDate(v, f);
38063         
38064         if(!d){
38065             this.validate();
38066             return;
38067         }
38068         
38069         this.setDay(d.format(this.dayFormat));
38070         this.setMonth(d.format(this.monthFormat));
38071         this.setYear(d.format(this.yearFormat));
38072         
38073         this.validate();
38074         
38075         return;
38076     },
38077     
38078     setDay : function(v)
38079     {
38080         this.dayField.setValue(v);
38081         this.inputEl.dom.value = this.getValue();
38082         this.validate();
38083         return;
38084     },
38085     
38086     setMonth : function(v)
38087     {
38088         this.monthField.setValue(v, true);
38089         this.inputEl.dom.value = this.getValue();
38090         this.validate();
38091         return;
38092     },
38093     
38094     setYear : function(v)
38095     {
38096         this.yearField.setValue(v);
38097         this.inputEl.dom.value = this.getValue();
38098         this.validate();
38099         return;
38100     },
38101     
38102     getDay : function()
38103     {
38104         return this.dayField.getValue();
38105     },
38106     
38107     getMonth : function()
38108     {
38109         return this.monthField.getValue();
38110     },
38111     
38112     getYear : function()
38113     {
38114         return this.yearField.getValue();
38115     },
38116     
38117     getValue : function()
38118     {
38119         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38120         
38121         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38122         
38123         return date;
38124     },
38125     
38126     reset : function()
38127     {
38128         this.setDay('');
38129         this.setMonth('');
38130         this.setYear('');
38131         this.inputEl.dom.value = '';
38132         this.validate();
38133         return;
38134     },
38135     
38136     validate : function()
38137     {
38138         var d = this.dayField.validate();
38139         var m = this.monthField.validate();
38140         var y = this.yearField.validate();
38141         
38142         var valid = true;
38143         
38144         if(
38145                 (!this.dayAllowBlank && !d) ||
38146                 (!this.monthAllowBlank && !m) ||
38147                 (!this.yearAllowBlank && !y)
38148         ){
38149             valid = false;
38150         }
38151         
38152         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38153             return valid;
38154         }
38155         
38156         if(valid){
38157             this.markValid();
38158             return valid;
38159         }
38160         
38161         this.markInvalid();
38162         
38163         return valid;
38164     },
38165     
38166     markValid : function()
38167     {
38168         
38169         var label = this.el.select('label', true).first();
38170         var icon = this.el.select('i.fa-star', true).first();
38171
38172         if(label && icon){
38173             icon.remove();
38174         }
38175         
38176         this.fireEvent('valid', this);
38177     },
38178     
38179      /**
38180      * Mark this field as invalid
38181      * @param {String} msg The validation message
38182      */
38183     markInvalid : function(msg)
38184     {
38185         
38186         var label = this.el.select('label', true).first();
38187         var icon = this.el.select('i.fa-star', true).first();
38188
38189         if(label && !icon){
38190             this.el.select('.roo-date-split-field-label', true).createChild({
38191                 tag : 'i',
38192                 cls : 'text-danger fa fa-lg fa-star',
38193                 tooltip : 'This field is required',
38194                 style : 'margin-right:5px;'
38195             }, label, true);
38196         }
38197         
38198         this.fireEvent('invalid', this, msg);
38199     },
38200     
38201     clearInvalid : function()
38202     {
38203         var label = this.el.select('label', true).first();
38204         var icon = this.el.select('i.fa-star', true).first();
38205
38206         if(label && icon){
38207             icon.remove();
38208         }
38209         
38210         this.fireEvent('valid', this);
38211     },
38212     
38213     getName: function()
38214     {
38215         return this.name;
38216     }
38217     
38218 });
38219
38220  
38221
38222 /**
38223  * @class Roo.bootstrap.LayoutMasonry
38224  * @extends Roo.bootstrap.Component
38225  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38226  * Bootstrap Layout Masonry class
38227  *
38228  * This is based on 
38229  * http://masonry.desandro.com
38230  *
38231  * The idea is to render all the bricks based on vertical width...
38232  *
38233  * The original code extends 'outlayer' - we might need to use that....
38234
38235  * @constructor
38236  * Create a new Element
38237  * @param {Object} config The config object
38238  */
38239
38240 Roo.bootstrap.LayoutMasonry = function(config){
38241     
38242     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38243     
38244     this.bricks = [];
38245     
38246     Roo.bootstrap.LayoutMasonry.register(this);
38247     
38248     this.addEvents({
38249         // raw events
38250         /**
38251          * @event layout
38252          * Fire after layout the items
38253          * @param {Roo.bootstrap.LayoutMasonry} this
38254          * @param {Roo.EventObject} e
38255          */
38256         "layout" : true
38257     });
38258     
38259 };
38260
38261 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38262     
38263     /**
38264      * @cfg {Boolean} isLayoutInstant = no animation?
38265      */   
38266     isLayoutInstant : false, // needed?
38267    
38268     /**
38269      * @cfg {Number} boxWidth  width of the columns
38270      */   
38271     boxWidth : 450,
38272     
38273       /**
38274      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38275      */   
38276     boxHeight : 0,
38277     
38278     /**
38279      * @cfg {Number} padWidth padding below box..
38280      */   
38281     padWidth : 10, 
38282     
38283     /**
38284      * @cfg {Number} gutter gutter width..
38285      */   
38286     gutter : 10,
38287     
38288      /**
38289      * @cfg {Number} maxCols maximum number of columns
38290      */   
38291     
38292     maxCols: 0,
38293     
38294     /**
38295      * @cfg {Boolean} isAutoInitial defalut true
38296      */   
38297     isAutoInitial : true, 
38298     
38299     containerWidth: 0,
38300     
38301     /**
38302      * @cfg {Boolean} isHorizontal defalut false
38303      */   
38304     isHorizontal : false, 
38305
38306     currentSize : null,
38307     
38308     tag: 'div',
38309     
38310     cls: '',
38311     
38312     bricks: null, //CompositeElement
38313     
38314     cols : 1,
38315     
38316     _isLayoutInited : false,
38317     
38318 //    isAlternative : false, // only use for vertical layout...
38319     
38320     /**
38321      * @cfg {Number} alternativePadWidth padding below box..
38322      */   
38323     alternativePadWidth : 50,
38324     
38325     selectedBrick : [],
38326     
38327     getAutoCreate : function(){
38328         
38329         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38330         
38331         var cfg = {
38332             tag: this.tag,
38333             cls: 'blog-masonary-wrapper ' + this.cls,
38334             cn : {
38335                 cls : 'mas-boxes masonary'
38336             }
38337         };
38338         
38339         return cfg;
38340     },
38341     
38342     getChildContainer: function( )
38343     {
38344         if (this.boxesEl) {
38345             return this.boxesEl;
38346         }
38347         
38348         this.boxesEl = this.el.select('.mas-boxes').first();
38349         
38350         return this.boxesEl;
38351     },
38352     
38353     
38354     initEvents : function()
38355     {
38356         var _this = this;
38357         
38358         if(this.isAutoInitial){
38359             Roo.log('hook children rendered');
38360             this.on('childrenrendered', function() {
38361                 Roo.log('children rendered');
38362                 _this.initial();
38363             } ,this);
38364         }
38365     },
38366     
38367     initial : function()
38368     {
38369         this.selectedBrick = [];
38370         
38371         this.currentSize = this.el.getBox(true);
38372         
38373         Roo.EventManager.onWindowResize(this.resize, this); 
38374
38375         if(!this.isAutoInitial){
38376             this.layout();
38377             return;
38378         }
38379         
38380         this.layout();
38381         
38382         return;
38383         //this.layout.defer(500,this);
38384         
38385     },
38386     
38387     resize : function()
38388     {
38389         var cs = this.el.getBox(true);
38390         
38391         if (
38392                 this.currentSize.width == cs.width && 
38393                 this.currentSize.x == cs.x && 
38394                 this.currentSize.height == cs.height && 
38395                 this.currentSize.y == cs.y 
38396         ) {
38397             Roo.log("no change in with or X or Y");
38398             return;
38399         }
38400         
38401         this.currentSize = cs;
38402         
38403         this.layout();
38404         
38405     },
38406     
38407     layout : function()
38408     {   
38409         this._resetLayout();
38410         
38411         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38412         
38413         this.layoutItems( isInstant );
38414       
38415         this._isLayoutInited = true;
38416         
38417         this.fireEvent('layout', this);
38418         
38419     },
38420     
38421     _resetLayout : function()
38422     {
38423         if(this.isHorizontal){
38424             this.horizontalMeasureColumns();
38425             return;
38426         }
38427         
38428         this.verticalMeasureColumns();
38429         
38430     },
38431     
38432     verticalMeasureColumns : function()
38433     {
38434         this.getContainerWidth();
38435         
38436 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38437 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38438 //            return;
38439 //        }
38440         
38441         var boxWidth = this.boxWidth + this.padWidth;
38442         
38443         if(this.containerWidth < this.boxWidth){
38444             boxWidth = this.containerWidth
38445         }
38446         
38447         var containerWidth = this.containerWidth;
38448         
38449         var cols = Math.floor(containerWidth / boxWidth);
38450         
38451         this.cols = Math.max( cols, 1 );
38452         
38453         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38454         
38455         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38456         
38457         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38458         
38459         this.colWidth = boxWidth + avail - this.padWidth;
38460         
38461         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38462         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38463     },
38464     
38465     horizontalMeasureColumns : function()
38466     {
38467         this.getContainerWidth();
38468         
38469         var boxWidth = this.boxWidth;
38470         
38471         if(this.containerWidth < boxWidth){
38472             boxWidth = this.containerWidth;
38473         }
38474         
38475         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38476         
38477         this.el.setHeight(boxWidth);
38478         
38479     },
38480     
38481     getContainerWidth : function()
38482     {
38483         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38484     },
38485     
38486     layoutItems : function( isInstant )
38487     {
38488         Roo.log(this.bricks);
38489         
38490         var items = Roo.apply([], this.bricks);
38491         
38492         if(this.isHorizontal){
38493             this._horizontalLayoutItems( items , isInstant );
38494             return;
38495         }
38496         
38497 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38498 //            this._verticalAlternativeLayoutItems( items , isInstant );
38499 //            return;
38500 //        }
38501         
38502         this._verticalLayoutItems( items , isInstant );
38503         
38504     },
38505     
38506     _verticalLayoutItems : function ( items , isInstant)
38507     {
38508         if ( !items || !items.length ) {
38509             return;
38510         }
38511         
38512         var standard = [
38513             ['xs', 'xs', 'xs', 'tall'],
38514             ['xs', 'xs', 'tall'],
38515             ['xs', 'xs', 'sm'],
38516             ['xs', 'xs', 'xs'],
38517             ['xs', 'tall'],
38518             ['xs', 'sm'],
38519             ['xs', 'xs'],
38520             ['xs'],
38521             
38522             ['sm', 'xs', 'xs'],
38523             ['sm', 'xs'],
38524             ['sm'],
38525             
38526             ['tall', 'xs', 'xs', 'xs'],
38527             ['tall', 'xs', 'xs'],
38528             ['tall', 'xs'],
38529             ['tall']
38530             
38531         ];
38532         
38533         var queue = [];
38534         
38535         var boxes = [];
38536         
38537         var box = [];
38538         
38539         Roo.each(items, function(item, k){
38540             
38541             switch (item.size) {
38542                 // these layouts take up a full box,
38543                 case 'md' :
38544                 case 'md-left' :
38545                 case 'md-right' :
38546                 case 'wide' :
38547                     
38548                     if(box.length){
38549                         boxes.push(box);
38550                         box = [];
38551                     }
38552                     
38553                     boxes.push([item]);
38554                     
38555                     break;
38556                     
38557                 case 'xs' :
38558                 case 'sm' :
38559                 case 'tall' :
38560                     
38561                     box.push(item);
38562                     
38563                     break;
38564                 default :
38565                     break;
38566                     
38567             }
38568             
38569         }, this);
38570         
38571         if(box.length){
38572             boxes.push(box);
38573             box = [];
38574         }
38575         
38576         var filterPattern = function(box, length)
38577         {
38578             if(!box.length){
38579                 return;
38580             }
38581             
38582             var match = false;
38583             
38584             var pattern = box.slice(0, length);
38585             
38586             var format = [];
38587             
38588             Roo.each(pattern, function(i){
38589                 format.push(i.size);
38590             }, this);
38591             
38592             Roo.each(standard, function(s){
38593                 
38594                 if(String(s) != String(format)){
38595                     return;
38596                 }
38597                 
38598                 match = true;
38599                 return false;
38600                 
38601             }, this);
38602             
38603             if(!match && length == 1){
38604                 return;
38605             }
38606             
38607             if(!match){
38608                 filterPattern(box, length - 1);
38609                 return;
38610             }
38611                 
38612             queue.push(pattern);
38613
38614             box = box.slice(length, box.length);
38615
38616             filterPattern(box, 4);
38617
38618             return;
38619             
38620         }
38621         
38622         Roo.each(boxes, function(box, k){
38623             
38624             if(!box.length){
38625                 return;
38626             }
38627             
38628             if(box.length == 1){
38629                 queue.push(box);
38630                 return;
38631             }
38632             
38633             filterPattern(box, 4);
38634             
38635         }, this);
38636         
38637         this._processVerticalLayoutQueue( queue, isInstant );
38638         
38639     },
38640     
38641 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38642 //    {
38643 //        if ( !items || !items.length ) {
38644 //            return;
38645 //        }
38646 //
38647 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38648 //        
38649 //    },
38650     
38651     _horizontalLayoutItems : function ( items , isInstant)
38652     {
38653         if ( !items || !items.length || items.length < 3) {
38654             return;
38655         }
38656         
38657         items.reverse();
38658         
38659         var eItems = items.slice(0, 3);
38660         
38661         items = items.slice(3, items.length);
38662         
38663         var standard = [
38664             ['xs', 'xs', 'xs', 'wide'],
38665             ['xs', 'xs', 'wide'],
38666             ['xs', 'xs', 'sm'],
38667             ['xs', 'xs', 'xs'],
38668             ['xs', 'wide'],
38669             ['xs', 'sm'],
38670             ['xs', 'xs'],
38671             ['xs'],
38672             
38673             ['sm', 'xs', 'xs'],
38674             ['sm', 'xs'],
38675             ['sm'],
38676             
38677             ['wide', 'xs', 'xs', 'xs'],
38678             ['wide', 'xs', 'xs'],
38679             ['wide', 'xs'],
38680             ['wide'],
38681             
38682             ['wide-thin']
38683         ];
38684         
38685         var queue = [];
38686         
38687         var boxes = [];
38688         
38689         var box = [];
38690         
38691         Roo.each(items, function(item, k){
38692             
38693             switch (item.size) {
38694                 case 'md' :
38695                 case 'md-left' :
38696                 case 'md-right' :
38697                 case 'tall' :
38698                     
38699                     if(box.length){
38700                         boxes.push(box);
38701                         box = [];
38702                     }
38703                     
38704                     boxes.push([item]);
38705                     
38706                     break;
38707                     
38708                 case 'xs' :
38709                 case 'sm' :
38710                 case 'wide' :
38711                 case 'wide-thin' :
38712                     
38713                     box.push(item);
38714                     
38715                     break;
38716                 default :
38717                     break;
38718                     
38719             }
38720             
38721         }, this);
38722         
38723         if(box.length){
38724             boxes.push(box);
38725             box = [];
38726         }
38727         
38728         var filterPattern = function(box, length)
38729         {
38730             if(!box.length){
38731                 return;
38732             }
38733             
38734             var match = false;
38735             
38736             var pattern = box.slice(0, length);
38737             
38738             var format = [];
38739             
38740             Roo.each(pattern, function(i){
38741                 format.push(i.size);
38742             }, this);
38743             
38744             Roo.each(standard, function(s){
38745                 
38746                 if(String(s) != String(format)){
38747                     return;
38748                 }
38749                 
38750                 match = true;
38751                 return false;
38752                 
38753             }, this);
38754             
38755             if(!match && length == 1){
38756                 return;
38757             }
38758             
38759             if(!match){
38760                 filterPattern(box, length - 1);
38761                 return;
38762             }
38763                 
38764             queue.push(pattern);
38765
38766             box = box.slice(length, box.length);
38767
38768             filterPattern(box, 4);
38769
38770             return;
38771             
38772         }
38773         
38774         Roo.each(boxes, function(box, k){
38775             
38776             if(!box.length){
38777                 return;
38778             }
38779             
38780             if(box.length == 1){
38781                 queue.push(box);
38782                 return;
38783             }
38784             
38785             filterPattern(box, 4);
38786             
38787         }, this);
38788         
38789         
38790         var prune = [];
38791         
38792         var pos = this.el.getBox(true);
38793         
38794         var minX = pos.x;
38795         
38796         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38797         
38798         var hit_end = false;
38799         
38800         Roo.each(queue, function(box){
38801             
38802             if(hit_end){
38803                 
38804                 Roo.each(box, function(b){
38805                 
38806                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38807                     b.el.hide();
38808
38809                 }, this);
38810
38811                 return;
38812             }
38813             
38814             var mx = 0;
38815             
38816             Roo.each(box, function(b){
38817                 
38818                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38819                 b.el.show();
38820
38821                 mx = Math.max(mx, b.x);
38822                 
38823             }, this);
38824             
38825             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38826             
38827             if(maxX < minX){
38828                 
38829                 Roo.each(box, function(b){
38830                 
38831                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38832                     b.el.hide();
38833                     
38834                 }, this);
38835                 
38836                 hit_end = true;
38837                 
38838                 return;
38839             }
38840             
38841             prune.push(box);
38842             
38843         }, this);
38844         
38845         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38846     },
38847     
38848     /** Sets position of item in DOM
38849     * @param {Element} item
38850     * @param {Number} x - horizontal position
38851     * @param {Number} y - vertical position
38852     * @param {Boolean} isInstant - disables transitions
38853     */
38854     _processVerticalLayoutQueue : function( queue, isInstant )
38855     {
38856         var pos = this.el.getBox(true);
38857         var x = pos.x;
38858         var y = pos.y;
38859         var maxY = [];
38860         
38861         for (var i = 0; i < this.cols; i++){
38862             maxY[i] = pos.y;
38863         }
38864         
38865         Roo.each(queue, function(box, k){
38866             
38867             var col = k % this.cols;
38868             
38869             Roo.each(box, function(b,kk){
38870                 
38871                 b.el.position('absolute');
38872                 
38873                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38874                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38875                 
38876                 if(b.size == 'md-left' || b.size == 'md-right'){
38877                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38878                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38879                 }
38880                 
38881                 b.el.setWidth(width);
38882                 b.el.setHeight(height);
38883                 // iframe?
38884                 b.el.select('iframe',true).setSize(width,height);
38885                 
38886             }, this);
38887             
38888             for (var i = 0; i < this.cols; i++){
38889                 
38890                 if(maxY[i] < maxY[col]){
38891                     col = i;
38892                     continue;
38893                 }
38894                 
38895                 col = Math.min(col, i);
38896                 
38897             }
38898             
38899             x = pos.x + col * (this.colWidth + this.padWidth);
38900             
38901             y = maxY[col];
38902             
38903             var positions = [];
38904             
38905             switch (box.length){
38906                 case 1 :
38907                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38908                     break;
38909                 case 2 :
38910                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38911                     break;
38912                 case 3 :
38913                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38914                     break;
38915                 case 4 :
38916                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38917                     break;
38918                 default :
38919                     break;
38920             }
38921             
38922             Roo.each(box, function(b,kk){
38923                 
38924                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38925                 
38926                 var sz = b.el.getSize();
38927                 
38928                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38929                 
38930             }, this);
38931             
38932         }, this);
38933         
38934         var mY = 0;
38935         
38936         for (var i = 0; i < this.cols; i++){
38937             mY = Math.max(mY, maxY[i]);
38938         }
38939         
38940         this.el.setHeight(mY - pos.y);
38941         
38942     },
38943     
38944 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38945 //    {
38946 //        var pos = this.el.getBox(true);
38947 //        var x = pos.x;
38948 //        var y = pos.y;
38949 //        var maxX = pos.right;
38950 //        
38951 //        var maxHeight = 0;
38952 //        
38953 //        Roo.each(items, function(item, k){
38954 //            
38955 //            var c = k % 2;
38956 //            
38957 //            item.el.position('absolute');
38958 //                
38959 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38960 //
38961 //            item.el.setWidth(width);
38962 //
38963 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38964 //
38965 //            item.el.setHeight(height);
38966 //            
38967 //            if(c == 0){
38968 //                item.el.setXY([x, y], isInstant ? false : true);
38969 //            } else {
38970 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38971 //            }
38972 //            
38973 //            y = y + height + this.alternativePadWidth;
38974 //            
38975 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38976 //            
38977 //        }, this);
38978 //        
38979 //        this.el.setHeight(maxHeight);
38980 //        
38981 //    },
38982     
38983     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38984     {
38985         var pos = this.el.getBox(true);
38986         
38987         var minX = pos.x;
38988         var minY = pos.y;
38989         
38990         var maxX = pos.right;
38991         
38992         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38993         
38994         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38995         
38996         Roo.each(queue, function(box, k){
38997             
38998             Roo.each(box, function(b, kk){
38999                 
39000                 b.el.position('absolute');
39001                 
39002                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39003                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39004                 
39005                 if(b.size == 'md-left' || b.size == 'md-right'){
39006                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39007                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39008                 }
39009                 
39010                 b.el.setWidth(width);
39011                 b.el.setHeight(height);
39012                 
39013             }, this);
39014             
39015             if(!box.length){
39016                 return;
39017             }
39018             
39019             var positions = [];
39020             
39021             switch (box.length){
39022                 case 1 :
39023                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39024                     break;
39025                 case 2 :
39026                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39027                     break;
39028                 case 3 :
39029                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39030                     break;
39031                 case 4 :
39032                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39033                     break;
39034                 default :
39035                     break;
39036             }
39037             
39038             Roo.each(box, function(b,kk){
39039                 
39040                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39041                 
39042                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39043                 
39044             }, this);
39045             
39046         }, this);
39047         
39048     },
39049     
39050     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39051     {
39052         Roo.each(eItems, function(b,k){
39053             
39054             b.size = (k == 0) ? 'sm' : 'xs';
39055             b.x = (k == 0) ? 2 : 1;
39056             b.y = (k == 0) ? 2 : 1;
39057             
39058             b.el.position('absolute');
39059             
39060             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39061                 
39062             b.el.setWidth(width);
39063             
39064             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39065             
39066             b.el.setHeight(height);
39067             
39068         }, this);
39069
39070         var positions = [];
39071         
39072         positions.push({
39073             x : maxX - this.unitWidth * 2 - this.gutter,
39074             y : minY
39075         });
39076         
39077         positions.push({
39078             x : maxX - this.unitWidth,
39079             y : minY + (this.unitWidth + this.gutter) * 2
39080         });
39081         
39082         positions.push({
39083             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39084             y : minY
39085         });
39086         
39087         Roo.each(eItems, function(b,k){
39088             
39089             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39090
39091         }, this);
39092         
39093     },
39094     
39095     getVerticalOneBoxColPositions : function(x, y, box)
39096     {
39097         var pos = [];
39098         
39099         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39100         
39101         if(box[0].size == 'md-left'){
39102             rand = 0;
39103         }
39104         
39105         if(box[0].size == 'md-right'){
39106             rand = 1;
39107         }
39108         
39109         pos.push({
39110             x : x + (this.unitWidth + this.gutter) * rand,
39111             y : y
39112         });
39113         
39114         return pos;
39115     },
39116     
39117     getVerticalTwoBoxColPositions : function(x, y, box)
39118     {
39119         var pos = [];
39120         
39121         if(box[0].size == 'xs'){
39122             
39123             pos.push({
39124                 x : x,
39125                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39126             });
39127
39128             pos.push({
39129                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39130                 y : y
39131             });
39132             
39133             return pos;
39134             
39135         }
39136         
39137         pos.push({
39138             x : x,
39139             y : y
39140         });
39141
39142         pos.push({
39143             x : x + (this.unitWidth + this.gutter) * 2,
39144             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39145         });
39146         
39147         return pos;
39148         
39149     },
39150     
39151     getVerticalThreeBoxColPositions : function(x, y, box)
39152     {
39153         var pos = [];
39154         
39155         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39156             
39157             pos.push({
39158                 x : x,
39159                 y : y
39160             });
39161
39162             pos.push({
39163                 x : x + (this.unitWidth + this.gutter) * 1,
39164                 y : y
39165             });
39166             
39167             pos.push({
39168                 x : x + (this.unitWidth + this.gutter) * 2,
39169                 y : y
39170             });
39171             
39172             return pos;
39173             
39174         }
39175         
39176         if(box[0].size == 'xs' && box[1].size == 'xs'){
39177             
39178             pos.push({
39179                 x : x,
39180                 y : y
39181             });
39182
39183             pos.push({
39184                 x : x,
39185                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39186             });
39187             
39188             pos.push({
39189                 x : x + (this.unitWidth + this.gutter) * 1,
39190                 y : y
39191             });
39192             
39193             return pos;
39194             
39195         }
39196         
39197         pos.push({
39198             x : x,
39199             y : y
39200         });
39201
39202         pos.push({
39203             x : x + (this.unitWidth + this.gutter) * 2,
39204             y : y
39205         });
39206
39207         pos.push({
39208             x : x + (this.unitWidth + this.gutter) * 2,
39209             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39210         });
39211             
39212         return pos;
39213         
39214     },
39215     
39216     getVerticalFourBoxColPositions : function(x, y, box)
39217     {
39218         var pos = [];
39219         
39220         if(box[0].size == 'xs'){
39221             
39222             pos.push({
39223                 x : x,
39224                 y : y
39225             });
39226
39227             pos.push({
39228                 x : x,
39229                 y : y + (this.unitHeight + this.gutter) * 1
39230             });
39231             
39232             pos.push({
39233                 x : x,
39234                 y : y + (this.unitHeight + this.gutter) * 2
39235             });
39236             
39237             pos.push({
39238                 x : x + (this.unitWidth + this.gutter) * 1,
39239                 y : y
39240             });
39241             
39242             return pos;
39243             
39244         }
39245         
39246         pos.push({
39247             x : x,
39248             y : y
39249         });
39250
39251         pos.push({
39252             x : x + (this.unitWidth + this.gutter) * 2,
39253             y : y
39254         });
39255
39256         pos.push({
39257             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39258             y : y + (this.unitHeight + this.gutter) * 1
39259         });
39260
39261         pos.push({
39262             x : x + (this.unitWidth + this.gutter) * 2,
39263             y : y + (this.unitWidth + this.gutter) * 2
39264         });
39265
39266         return pos;
39267         
39268     },
39269     
39270     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39271     {
39272         var pos = [];
39273         
39274         if(box[0].size == 'md-left'){
39275             pos.push({
39276                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39277                 y : minY
39278             });
39279             
39280             return pos;
39281         }
39282         
39283         if(box[0].size == 'md-right'){
39284             pos.push({
39285                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39286                 y : minY + (this.unitWidth + this.gutter) * 1
39287             });
39288             
39289             return pos;
39290         }
39291         
39292         var rand = Math.floor(Math.random() * (4 - box[0].y));
39293         
39294         pos.push({
39295             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39296             y : minY + (this.unitWidth + this.gutter) * rand
39297         });
39298         
39299         return pos;
39300         
39301     },
39302     
39303     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39304     {
39305         var pos = [];
39306         
39307         if(box[0].size == 'xs'){
39308             
39309             pos.push({
39310                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39311                 y : minY
39312             });
39313
39314             pos.push({
39315                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39316                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39317             });
39318             
39319             return pos;
39320             
39321         }
39322         
39323         pos.push({
39324             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39325             y : minY
39326         });
39327
39328         pos.push({
39329             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39330             y : minY + (this.unitWidth + this.gutter) * 2
39331         });
39332         
39333         return pos;
39334         
39335     },
39336     
39337     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39338     {
39339         var pos = [];
39340         
39341         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39342             
39343             pos.push({
39344                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39345                 y : minY
39346             });
39347
39348             pos.push({
39349                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39350                 y : minY + (this.unitWidth + this.gutter) * 1
39351             });
39352             
39353             pos.push({
39354                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39355                 y : minY + (this.unitWidth + this.gutter) * 2
39356             });
39357             
39358             return pos;
39359             
39360         }
39361         
39362         if(box[0].size == 'xs' && box[1].size == 'xs'){
39363             
39364             pos.push({
39365                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39366                 y : minY
39367             });
39368
39369             pos.push({
39370                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39371                 y : minY
39372             });
39373             
39374             pos.push({
39375                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39376                 y : minY + (this.unitWidth + this.gutter) * 1
39377             });
39378             
39379             return pos;
39380             
39381         }
39382         
39383         pos.push({
39384             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39385             y : minY
39386         });
39387
39388         pos.push({
39389             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39390             y : minY + (this.unitWidth + this.gutter) * 2
39391         });
39392
39393         pos.push({
39394             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39395             y : minY + (this.unitWidth + this.gutter) * 2
39396         });
39397             
39398         return pos;
39399         
39400     },
39401     
39402     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39403     {
39404         var pos = [];
39405         
39406         if(box[0].size == 'xs'){
39407             
39408             pos.push({
39409                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39410                 y : minY
39411             });
39412
39413             pos.push({
39414                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39415                 y : minY
39416             });
39417             
39418             pos.push({
39419                 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),
39420                 y : minY
39421             });
39422             
39423             pos.push({
39424                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39425                 y : minY + (this.unitWidth + this.gutter) * 1
39426             });
39427             
39428             return pos;
39429             
39430         }
39431         
39432         pos.push({
39433             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39434             y : minY
39435         });
39436         
39437         pos.push({
39438             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39439             y : minY + (this.unitWidth + this.gutter) * 2
39440         });
39441         
39442         pos.push({
39443             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39444             y : minY + (this.unitWidth + this.gutter) * 2
39445         });
39446         
39447         pos.push({
39448             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),
39449             y : minY + (this.unitWidth + this.gutter) * 2
39450         });
39451
39452         return pos;
39453         
39454     },
39455     
39456     /**
39457     * remove a Masonry Brick
39458     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39459     */
39460     removeBrick : function(brick_id)
39461     {
39462         if (!brick_id) {
39463             return;
39464         }
39465         
39466         for (var i = 0; i<this.bricks.length; i++) {
39467             if (this.bricks[i].id == brick_id) {
39468                 this.bricks.splice(i,1);
39469                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39470                 this.initial();
39471             }
39472         }
39473     },
39474     
39475     /**
39476     * adds a Masonry Brick
39477     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39478     */
39479     addBrick : function(cfg)
39480     {
39481         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39482         //this.register(cn);
39483         cn.parentId = this.id;
39484         cn.render(this.el);
39485         return cn;
39486     },
39487     
39488     /**
39489     * register a Masonry Brick
39490     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39491     */
39492     
39493     register : function(brick)
39494     {
39495         this.bricks.push(brick);
39496         brick.masonryId = this.id;
39497     },
39498     
39499     /**
39500     * clear all the Masonry Brick
39501     */
39502     clearAll : function()
39503     {
39504         this.bricks = [];
39505         //this.getChildContainer().dom.innerHTML = "";
39506         this.el.dom.innerHTML = '';
39507     },
39508     
39509     getSelected : function()
39510     {
39511         if (!this.selectedBrick) {
39512             return false;
39513         }
39514         
39515         return this.selectedBrick;
39516     }
39517 });
39518
39519 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39520     
39521     groups: {},
39522      /**
39523     * register a Masonry Layout
39524     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39525     */
39526     
39527     register : function(layout)
39528     {
39529         this.groups[layout.id] = layout;
39530     },
39531     /**
39532     * fetch a  Masonry Layout based on the masonry layout ID
39533     * @param {string} the masonry layout to add
39534     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39535     */
39536     
39537     get: function(layout_id) {
39538         if (typeof(this.groups[layout_id]) == 'undefined') {
39539             return false;
39540         }
39541         return this.groups[layout_id] ;
39542     }
39543     
39544     
39545     
39546 });
39547
39548  
39549
39550  /**
39551  *
39552  * This is based on 
39553  * http://masonry.desandro.com
39554  *
39555  * The idea is to render all the bricks based on vertical width...
39556  *
39557  * The original code extends 'outlayer' - we might need to use that....
39558  * 
39559  */
39560
39561
39562 /**
39563  * @class Roo.bootstrap.LayoutMasonryAuto
39564  * @extends Roo.bootstrap.Component
39565  * Bootstrap Layout Masonry class
39566  * 
39567  * @constructor
39568  * Create a new Element
39569  * @param {Object} config The config object
39570  */
39571
39572 Roo.bootstrap.LayoutMasonryAuto = function(config){
39573     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39574 };
39575
39576 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39577     
39578       /**
39579      * @cfg {Boolean} isFitWidth  - resize the width..
39580      */   
39581     isFitWidth : false,  // options..
39582     /**
39583      * @cfg {Boolean} isOriginLeft = left align?
39584      */   
39585     isOriginLeft : true,
39586     /**
39587      * @cfg {Boolean} isOriginTop = top align?
39588      */   
39589     isOriginTop : false,
39590     /**
39591      * @cfg {Boolean} isLayoutInstant = no animation?
39592      */   
39593     isLayoutInstant : false, // needed?
39594     /**
39595      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39596      */   
39597     isResizingContainer : true,
39598     /**
39599      * @cfg {Number} columnWidth  width of the columns 
39600      */   
39601     
39602     columnWidth : 0,
39603     
39604     /**
39605      * @cfg {Number} maxCols maximum number of columns
39606      */   
39607     
39608     maxCols: 0,
39609     /**
39610      * @cfg {Number} padHeight padding below box..
39611      */   
39612     
39613     padHeight : 10, 
39614     
39615     /**
39616      * @cfg {Boolean} isAutoInitial defalut true
39617      */   
39618     
39619     isAutoInitial : true, 
39620     
39621     // private?
39622     gutter : 0,
39623     
39624     containerWidth: 0,
39625     initialColumnWidth : 0,
39626     currentSize : null,
39627     
39628     colYs : null, // array.
39629     maxY : 0,
39630     padWidth: 10,
39631     
39632     
39633     tag: 'div',
39634     cls: '',
39635     bricks: null, //CompositeElement
39636     cols : 0, // array?
39637     // element : null, // wrapped now this.el
39638     _isLayoutInited : null, 
39639     
39640     
39641     getAutoCreate : function(){
39642         
39643         var cfg = {
39644             tag: this.tag,
39645             cls: 'blog-masonary-wrapper ' + this.cls,
39646             cn : {
39647                 cls : 'mas-boxes masonary'
39648             }
39649         };
39650         
39651         return cfg;
39652     },
39653     
39654     getChildContainer: function( )
39655     {
39656         if (this.boxesEl) {
39657             return this.boxesEl;
39658         }
39659         
39660         this.boxesEl = this.el.select('.mas-boxes').first();
39661         
39662         return this.boxesEl;
39663     },
39664     
39665     
39666     initEvents : function()
39667     {
39668         var _this = this;
39669         
39670         if(this.isAutoInitial){
39671             Roo.log('hook children rendered');
39672             this.on('childrenrendered', function() {
39673                 Roo.log('children rendered');
39674                 _this.initial();
39675             } ,this);
39676         }
39677         
39678     },
39679     
39680     initial : function()
39681     {
39682         this.reloadItems();
39683
39684         this.currentSize = this.el.getBox(true);
39685
39686         /// was window resize... - let's see if this works..
39687         Roo.EventManager.onWindowResize(this.resize, this); 
39688
39689         if(!this.isAutoInitial){
39690             this.layout();
39691             return;
39692         }
39693         
39694         this.layout.defer(500,this);
39695     },
39696     
39697     reloadItems: function()
39698     {
39699         this.bricks = this.el.select('.masonry-brick', true);
39700         
39701         this.bricks.each(function(b) {
39702             //Roo.log(b.getSize());
39703             if (!b.attr('originalwidth')) {
39704                 b.attr('originalwidth',  b.getSize().width);
39705             }
39706             
39707         });
39708         
39709         Roo.log(this.bricks.elements.length);
39710     },
39711     
39712     resize : function()
39713     {
39714         Roo.log('resize');
39715         var cs = this.el.getBox(true);
39716         
39717         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39718             Roo.log("no change in with or X");
39719             return;
39720         }
39721         this.currentSize = cs;
39722         this.layout();
39723     },
39724     
39725     layout : function()
39726     {
39727          Roo.log('layout');
39728         this._resetLayout();
39729         //this._manageStamps();
39730       
39731         // don't animate first layout
39732         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39733         this.layoutItems( isInstant );
39734       
39735         // flag for initalized
39736         this._isLayoutInited = true;
39737     },
39738     
39739     layoutItems : function( isInstant )
39740     {
39741         //var items = this._getItemsForLayout( this.items );
39742         // original code supports filtering layout items.. we just ignore it..
39743         
39744         this._layoutItems( this.bricks , isInstant );
39745       
39746         this._postLayout();
39747     },
39748     _layoutItems : function ( items , isInstant)
39749     {
39750        //this.fireEvent( 'layout', this, items );
39751     
39752
39753         if ( !items || !items.elements.length ) {
39754           // no items, emit event with empty array
39755             return;
39756         }
39757
39758         var queue = [];
39759         items.each(function(item) {
39760             Roo.log("layout item");
39761             Roo.log(item);
39762             // get x/y object from method
39763             var position = this._getItemLayoutPosition( item );
39764             // enqueue
39765             position.item = item;
39766             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39767             queue.push( position );
39768         }, this);
39769       
39770         this._processLayoutQueue( queue );
39771     },
39772     /** Sets position of item in DOM
39773     * @param {Element} item
39774     * @param {Number} x - horizontal position
39775     * @param {Number} y - vertical position
39776     * @param {Boolean} isInstant - disables transitions
39777     */
39778     _processLayoutQueue : function( queue )
39779     {
39780         for ( var i=0, len = queue.length; i < len; i++ ) {
39781             var obj = queue[i];
39782             obj.item.position('absolute');
39783             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39784         }
39785     },
39786       
39787     
39788     /**
39789     * Any logic you want to do after each layout,
39790     * i.e. size the container
39791     */
39792     _postLayout : function()
39793     {
39794         this.resizeContainer();
39795     },
39796     
39797     resizeContainer : function()
39798     {
39799         if ( !this.isResizingContainer ) {
39800             return;
39801         }
39802         var size = this._getContainerSize();
39803         if ( size ) {
39804             this.el.setSize(size.width,size.height);
39805             this.boxesEl.setSize(size.width,size.height);
39806         }
39807     },
39808     
39809     
39810     
39811     _resetLayout : function()
39812     {
39813         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39814         this.colWidth = this.el.getWidth();
39815         //this.gutter = this.el.getWidth(); 
39816         
39817         this.measureColumns();
39818
39819         // reset column Y
39820         var i = this.cols;
39821         this.colYs = [];
39822         while (i--) {
39823             this.colYs.push( 0 );
39824         }
39825     
39826         this.maxY = 0;
39827     },
39828
39829     measureColumns : function()
39830     {
39831         this.getContainerWidth();
39832       // if columnWidth is 0, default to outerWidth of first item
39833         if ( !this.columnWidth ) {
39834             var firstItem = this.bricks.first();
39835             Roo.log(firstItem);
39836             this.columnWidth  = this.containerWidth;
39837             if (firstItem && firstItem.attr('originalwidth') ) {
39838                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39839             }
39840             // columnWidth fall back to item of first element
39841             Roo.log("set column width?");
39842                         this.initialColumnWidth = this.columnWidth  ;
39843
39844             // if first elem has no width, default to size of container
39845             
39846         }
39847         
39848         
39849         if (this.initialColumnWidth) {
39850             this.columnWidth = this.initialColumnWidth;
39851         }
39852         
39853         
39854             
39855         // column width is fixed at the top - however if container width get's smaller we should
39856         // reduce it...
39857         
39858         // this bit calcs how man columns..
39859             
39860         var columnWidth = this.columnWidth += this.gutter;
39861       
39862         // calculate columns
39863         var containerWidth = this.containerWidth + this.gutter;
39864         
39865         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39866         // fix rounding errors, typically with gutters
39867         var excess = columnWidth - containerWidth % columnWidth;
39868         
39869         
39870         // if overshoot is less than a pixel, round up, otherwise floor it
39871         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39872         cols = Math[ mathMethod ]( cols );
39873         this.cols = Math.max( cols, 1 );
39874         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39875         
39876          // padding positioning..
39877         var totalColWidth = this.cols * this.columnWidth;
39878         var padavail = this.containerWidth - totalColWidth;
39879         // so for 2 columns - we need 3 'pads'
39880         
39881         var padNeeded = (1+this.cols) * this.padWidth;
39882         
39883         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39884         
39885         this.columnWidth += padExtra
39886         //this.padWidth = Math.floor(padavail /  ( this.cols));
39887         
39888         // adjust colum width so that padding is fixed??
39889         
39890         // we have 3 columns ... total = width * 3
39891         // we have X left over... that should be used by 
39892         
39893         //if (this.expandC) {
39894             
39895         //}
39896         
39897         
39898         
39899     },
39900     
39901     getContainerWidth : function()
39902     {
39903        /* // container is parent if fit width
39904         var container = this.isFitWidth ? this.element.parentNode : this.element;
39905         // check that this.size and size are there
39906         // IE8 triggers resize on body size change, so they might not be
39907         
39908         var size = getSize( container );  //FIXME
39909         this.containerWidth = size && size.innerWidth; //FIXME
39910         */
39911          
39912         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39913         
39914     },
39915     
39916     _getItemLayoutPosition : function( item )  // what is item?
39917     {
39918         // we resize the item to our columnWidth..
39919       
39920         item.setWidth(this.columnWidth);
39921         item.autoBoxAdjust  = false;
39922         
39923         var sz = item.getSize();
39924  
39925         // how many columns does this brick span
39926         var remainder = this.containerWidth % this.columnWidth;
39927         
39928         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39929         // round if off by 1 pixel, otherwise use ceil
39930         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39931         colSpan = Math.min( colSpan, this.cols );
39932         
39933         // normally this should be '1' as we dont' currently allow multi width columns..
39934         
39935         var colGroup = this._getColGroup( colSpan );
39936         // get the minimum Y value from the columns
39937         var minimumY = Math.min.apply( Math, colGroup );
39938         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39939         
39940         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39941          
39942         // position the brick
39943         var position = {
39944             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39945             y: this.currentSize.y + minimumY + this.padHeight
39946         };
39947         
39948         Roo.log(position);
39949         // apply setHeight to necessary columns
39950         var setHeight = minimumY + sz.height + this.padHeight;
39951         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39952         
39953         var setSpan = this.cols + 1 - colGroup.length;
39954         for ( var i = 0; i < setSpan; i++ ) {
39955           this.colYs[ shortColIndex + i ] = setHeight ;
39956         }
39957       
39958         return position;
39959     },
39960     
39961     /**
39962      * @param {Number} colSpan - number of columns the element spans
39963      * @returns {Array} colGroup
39964      */
39965     _getColGroup : function( colSpan )
39966     {
39967         if ( colSpan < 2 ) {
39968           // if brick spans only one column, use all the column Ys
39969           return this.colYs;
39970         }
39971       
39972         var colGroup = [];
39973         // how many different places could this brick fit horizontally
39974         var groupCount = this.cols + 1 - colSpan;
39975         // for each group potential horizontal position
39976         for ( var i = 0; i < groupCount; i++ ) {
39977           // make an array of colY values for that one group
39978           var groupColYs = this.colYs.slice( i, i + colSpan );
39979           // and get the max value of the array
39980           colGroup[i] = Math.max.apply( Math, groupColYs );
39981         }
39982         return colGroup;
39983     },
39984     /*
39985     _manageStamp : function( stamp )
39986     {
39987         var stampSize =  stamp.getSize();
39988         var offset = stamp.getBox();
39989         // get the columns that this stamp affects
39990         var firstX = this.isOriginLeft ? offset.x : offset.right;
39991         var lastX = firstX + stampSize.width;
39992         var firstCol = Math.floor( firstX / this.columnWidth );
39993         firstCol = Math.max( 0, firstCol );
39994         
39995         var lastCol = Math.floor( lastX / this.columnWidth );
39996         // lastCol should not go over if multiple of columnWidth #425
39997         lastCol -= lastX % this.columnWidth ? 0 : 1;
39998         lastCol = Math.min( this.cols - 1, lastCol );
39999         
40000         // set colYs to bottom of the stamp
40001         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40002             stampSize.height;
40003             
40004         for ( var i = firstCol; i <= lastCol; i++ ) {
40005           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40006         }
40007     },
40008     */
40009     
40010     _getContainerSize : function()
40011     {
40012         this.maxY = Math.max.apply( Math, this.colYs );
40013         var size = {
40014             height: this.maxY
40015         };
40016       
40017         if ( this.isFitWidth ) {
40018             size.width = this._getContainerFitWidth();
40019         }
40020       
40021         return size;
40022     },
40023     
40024     _getContainerFitWidth : function()
40025     {
40026         var unusedCols = 0;
40027         // count unused columns
40028         var i = this.cols;
40029         while ( --i ) {
40030           if ( this.colYs[i] !== 0 ) {
40031             break;
40032           }
40033           unusedCols++;
40034         }
40035         // fit container to columns that have been used
40036         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40037     },
40038     
40039     needsResizeLayout : function()
40040     {
40041         var previousWidth = this.containerWidth;
40042         this.getContainerWidth();
40043         return previousWidth !== this.containerWidth;
40044     }
40045  
40046 });
40047
40048  
40049
40050  /*
40051  * - LGPL
40052  *
40053  * element
40054  * 
40055  */
40056
40057 /**
40058  * @class Roo.bootstrap.MasonryBrick
40059  * @extends Roo.bootstrap.Component
40060  * Bootstrap MasonryBrick class
40061  * 
40062  * @constructor
40063  * Create a new MasonryBrick
40064  * @param {Object} config The config object
40065  */
40066
40067 Roo.bootstrap.MasonryBrick = function(config){
40068     
40069     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40070     
40071     Roo.bootstrap.MasonryBrick.register(this);
40072     
40073     this.addEvents({
40074         // raw events
40075         /**
40076          * @event click
40077          * When a MasonryBrick is clcik
40078          * @param {Roo.bootstrap.MasonryBrick} this
40079          * @param {Roo.EventObject} e
40080          */
40081         "click" : true
40082     });
40083 };
40084
40085 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40086     
40087     /**
40088      * @cfg {String} title
40089      */   
40090     title : '',
40091     /**
40092      * @cfg {String} html
40093      */   
40094     html : '',
40095     /**
40096      * @cfg {String} bgimage
40097      */   
40098     bgimage : '',
40099     /**
40100      * @cfg {String} videourl
40101      */   
40102     videourl : '',
40103     /**
40104      * @cfg {String} cls
40105      */   
40106     cls : '',
40107     /**
40108      * @cfg {String} href
40109      */   
40110     href : '',
40111     /**
40112      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40113      */   
40114     size : 'xs',
40115     
40116     /**
40117      * @cfg {String} placetitle (center|bottom)
40118      */   
40119     placetitle : '',
40120     
40121     /**
40122      * @cfg {Boolean} isFitContainer defalut true
40123      */   
40124     isFitContainer : true, 
40125     
40126     /**
40127      * @cfg {Boolean} preventDefault defalut false
40128      */   
40129     preventDefault : false, 
40130     
40131     /**
40132      * @cfg {Boolean} inverse defalut false
40133      */   
40134     maskInverse : false, 
40135     
40136     getAutoCreate : function()
40137     {
40138         if(!this.isFitContainer){
40139             return this.getSplitAutoCreate();
40140         }
40141         
40142         var cls = 'masonry-brick masonry-brick-full';
40143         
40144         if(this.href.length){
40145             cls += ' masonry-brick-link';
40146         }
40147         
40148         if(this.bgimage.length){
40149             cls += ' masonry-brick-image';
40150         }
40151         
40152         if(this.maskInverse){
40153             cls += ' mask-inverse';
40154         }
40155         
40156         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40157             cls += ' enable-mask';
40158         }
40159         
40160         if(this.size){
40161             cls += ' masonry-' + this.size + '-brick';
40162         }
40163         
40164         if(this.placetitle.length){
40165             
40166             switch (this.placetitle) {
40167                 case 'center' :
40168                     cls += ' masonry-center-title';
40169                     break;
40170                 case 'bottom' :
40171                     cls += ' masonry-bottom-title';
40172                     break;
40173                 default:
40174                     break;
40175             }
40176             
40177         } else {
40178             if(!this.html.length && !this.bgimage.length){
40179                 cls += ' masonry-center-title';
40180             }
40181
40182             if(!this.html.length && this.bgimage.length){
40183                 cls += ' masonry-bottom-title';
40184             }
40185         }
40186         
40187         if(this.cls){
40188             cls += ' ' + this.cls;
40189         }
40190         
40191         var cfg = {
40192             tag: (this.href.length) ? 'a' : 'div',
40193             cls: cls,
40194             cn: [
40195                 {
40196                     tag: 'div',
40197                     cls: 'masonry-brick-mask'
40198                 },
40199                 {
40200                     tag: 'div',
40201                     cls: 'masonry-brick-paragraph',
40202                     cn: []
40203                 }
40204             ]
40205         };
40206         
40207         if(this.href.length){
40208             cfg.href = this.href;
40209         }
40210         
40211         var cn = cfg.cn[1].cn;
40212         
40213         if(this.title.length){
40214             cn.push({
40215                 tag: 'h4',
40216                 cls: 'masonry-brick-title',
40217                 html: this.title
40218             });
40219         }
40220         
40221         if(this.html.length){
40222             cn.push({
40223                 tag: 'p',
40224                 cls: 'masonry-brick-text',
40225                 html: this.html
40226             });
40227         }
40228         
40229         if (!this.title.length && !this.html.length) {
40230             cfg.cn[1].cls += ' hide';
40231         }
40232         
40233         if(this.bgimage.length){
40234             cfg.cn.push({
40235                 tag: 'img',
40236                 cls: 'masonry-brick-image-view',
40237                 src: this.bgimage
40238             });
40239         }
40240         
40241         if(this.videourl.length){
40242             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40243             // youtube support only?
40244             cfg.cn.push({
40245                 tag: 'iframe',
40246                 cls: 'masonry-brick-image-view',
40247                 src: vurl,
40248                 frameborder : 0,
40249                 allowfullscreen : true
40250             });
40251         }
40252         
40253         return cfg;
40254         
40255     },
40256     
40257     getSplitAutoCreate : function()
40258     {
40259         var cls = 'masonry-brick masonry-brick-split';
40260         
40261         if(this.href.length){
40262             cls += ' masonry-brick-link';
40263         }
40264         
40265         if(this.bgimage.length){
40266             cls += ' masonry-brick-image';
40267         }
40268         
40269         if(this.size){
40270             cls += ' masonry-' + this.size + '-brick';
40271         }
40272         
40273         switch (this.placetitle) {
40274             case 'center' :
40275                 cls += ' masonry-center-title';
40276                 break;
40277             case 'bottom' :
40278                 cls += ' masonry-bottom-title';
40279                 break;
40280             default:
40281                 if(!this.bgimage.length){
40282                     cls += ' masonry-center-title';
40283                 }
40284
40285                 if(this.bgimage.length){
40286                     cls += ' masonry-bottom-title';
40287                 }
40288                 break;
40289         }
40290         
40291         if(this.cls){
40292             cls += ' ' + this.cls;
40293         }
40294         
40295         var cfg = {
40296             tag: (this.href.length) ? 'a' : 'div',
40297             cls: cls,
40298             cn: [
40299                 {
40300                     tag: 'div',
40301                     cls: 'masonry-brick-split-head',
40302                     cn: [
40303                         {
40304                             tag: 'div',
40305                             cls: 'masonry-brick-paragraph',
40306                             cn: []
40307                         }
40308                     ]
40309                 },
40310                 {
40311                     tag: 'div',
40312                     cls: 'masonry-brick-split-body',
40313                     cn: []
40314                 }
40315             ]
40316         };
40317         
40318         if(this.href.length){
40319             cfg.href = this.href;
40320         }
40321         
40322         if(this.title.length){
40323             cfg.cn[0].cn[0].cn.push({
40324                 tag: 'h4',
40325                 cls: 'masonry-brick-title',
40326                 html: this.title
40327             });
40328         }
40329         
40330         if(this.html.length){
40331             cfg.cn[1].cn.push({
40332                 tag: 'p',
40333                 cls: 'masonry-brick-text',
40334                 html: this.html
40335             });
40336         }
40337
40338         if(this.bgimage.length){
40339             cfg.cn[0].cn.push({
40340                 tag: 'img',
40341                 cls: 'masonry-brick-image-view',
40342                 src: this.bgimage
40343             });
40344         }
40345         
40346         if(this.videourl.length){
40347             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40348             // youtube support only?
40349             cfg.cn[0].cn.cn.push({
40350                 tag: 'iframe',
40351                 cls: 'masonry-brick-image-view',
40352                 src: vurl,
40353                 frameborder : 0,
40354                 allowfullscreen : true
40355             });
40356         }
40357         
40358         return cfg;
40359     },
40360     
40361     initEvents: function() 
40362     {
40363         switch (this.size) {
40364             case 'xs' :
40365                 this.x = 1;
40366                 this.y = 1;
40367                 break;
40368             case 'sm' :
40369                 this.x = 2;
40370                 this.y = 2;
40371                 break;
40372             case 'md' :
40373             case 'md-left' :
40374             case 'md-right' :
40375                 this.x = 3;
40376                 this.y = 3;
40377                 break;
40378             case 'tall' :
40379                 this.x = 2;
40380                 this.y = 3;
40381                 break;
40382             case 'wide' :
40383                 this.x = 3;
40384                 this.y = 2;
40385                 break;
40386             case 'wide-thin' :
40387                 this.x = 3;
40388                 this.y = 1;
40389                 break;
40390                         
40391             default :
40392                 break;
40393         }
40394         
40395         if(Roo.isTouch){
40396             this.el.on('touchstart', this.onTouchStart, this);
40397             this.el.on('touchmove', this.onTouchMove, this);
40398             this.el.on('touchend', this.onTouchEnd, this);
40399             this.el.on('contextmenu', this.onContextMenu, this);
40400         } else {
40401             this.el.on('mouseenter'  ,this.enter, this);
40402             this.el.on('mouseleave', this.leave, this);
40403             this.el.on('click', this.onClick, this);
40404         }
40405         
40406         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40407             this.parent().bricks.push(this);   
40408         }
40409         
40410     },
40411     
40412     onClick: function(e, el)
40413     {
40414         var time = this.endTimer - this.startTimer;
40415         // Roo.log(e.preventDefault());
40416         if(Roo.isTouch){
40417             if(time > 1000){
40418                 e.preventDefault();
40419                 return;
40420             }
40421         }
40422         
40423         if(!this.preventDefault){
40424             return;
40425         }
40426         
40427         e.preventDefault();
40428         
40429         if (this.activeClass != '') {
40430             this.selectBrick();
40431         }
40432         
40433         this.fireEvent('click', this, e);
40434     },
40435     
40436     enter: function(e, el)
40437     {
40438         e.preventDefault();
40439         
40440         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40441             return;
40442         }
40443         
40444         if(this.bgimage.length && this.html.length){
40445             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40446         }
40447     },
40448     
40449     leave: function(e, el)
40450     {
40451         e.preventDefault();
40452         
40453         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40454             return;
40455         }
40456         
40457         if(this.bgimage.length && this.html.length){
40458             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40459         }
40460     },
40461     
40462     onTouchStart: function(e, el)
40463     {
40464 //        e.preventDefault();
40465         
40466         this.touchmoved = false;
40467         
40468         if(!this.isFitContainer){
40469             return;
40470         }
40471         
40472         if(!this.bgimage.length || !this.html.length){
40473             return;
40474         }
40475         
40476         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40477         
40478         this.timer = new Date().getTime();
40479         
40480     },
40481     
40482     onTouchMove: function(e, el)
40483     {
40484         this.touchmoved = true;
40485     },
40486     
40487     onContextMenu : function(e,el)
40488     {
40489         e.preventDefault();
40490         e.stopPropagation();
40491         return false;
40492     },
40493     
40494     onTouchEnd: function(e, el)
40495     {
40496 //        e.preventDefault();
40497         
40498         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40499         
40500             this.leave(e,el);
40501             
40502             return;
40503         }
40504         
40505         if(!this.bgimage.length || !this.html.length){
40506             
40507             if(this.href.length){
40508                 window.location.href = this.href;
40509             }
40510             
40511             return;
40512         }
40513         
40514         if(!this.isFitContainer){
40515             return;
40516         }
40517         
40518         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40519         
40520         window.location.href = this.href;
40521     },
40522     
40523     //selection on single brick only
40524     selectBrick : function() {
40525         
40526         if (!this.parentId) {
40527             return;
40528         }
40529         
40530         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40531         var index = m.selectedBrick.indexOf(this.id);
40532         
40533         if ( index > -1) {
40534             m.selectedBrick.splice(index,1);
40535             this.el.removeClass(this.activeClass);
40536             return;
40537         }
40538         
40539         for(var i = 0; i < m.selectedBrick.length; i++) {
40540             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40541             b.el.removeClass(b.activeClass);
40542         }
40543         
40544         m.selectedBrick = [];
40545         
40546         m.selectedBrick.push(this.id);
40547         this.el.addClass(this.activeClass);
40548         return;
40549     },
40550     
40551     isSelected : function(){
40552         return this.el.hasClass(this.activeClass);
40553         
40554     }
40555 });
40556
40557 Roo.apply(Roo.bootstrap.MasonryBrick, {
40558     
40559     //groups: {},
40560     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40561      /**
40562     * register a Masonry Brick
40563     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40564     */
40565     
40566     register : function(brick)
40567     {
40568         //this.groups[brick.id] = brick;
40569         this.groups.add(brick.id, brick);
40570     },
40571     /**
40572     * fetch a  masonry brick based on the masonry brick ID
40573     * @param {string} the masonry brick to add
40574     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40575     */
40576     
40577     get: function(brick_id) 
40578     {
40579         // if (typeof(this.groups[brick_id]) == 'undefined') {
40580         //     return false;
40581         // }
40582         // return this.groups[brick_id] ;
40583         
40584         if(this.groups.key(brick_id)) {
40585             return this.groups.key(brick_id);
40586         }
40587         
40588         return false;
40589     }
40590     
40591     
40592     
40593 });
40594
40595  /*
40596  * - LGPL
40597  *
40598  * element
40599  * 
40600  */
40601
40602 /**
40603  * @class Roo.bootstrap.Brick
40604  * @extends Roo.bootstrap.Component
40605  * Bootstrap Brick class
40606  * 
40607  * @constructor
40608  * Create a new Brick
40609  * @param {Object} config The config object
40610  */
40611
40612 Roo.bootstrap.Brick = function(config){
40613     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40614     
40615     this.addEvents({
40616         // raw events
40617         /**
40618          * @event click
40619          * When a Brick is click
40620          * @param {Roo.bootstrap.Brick} this
40621          * @param {Roo.EventObject} e
40622          */
40623         "click" : true
40624     });
40625 };
40626
40627 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40628     
40629     /**
40630      * @cfg {String} title
40631      */   
40632     title : '',
40633     /**
40634      * @cfg {String} html
40635      */   
40636     html : '',
40637     /**
40638      * @cfg {String} bgimage
40639      */   
40640     bgimage : '',
40641     /**
40642      * @cfg {String} cls
40643      */   
40644     cls : '',
40645     /**
40646      * @cfg {String} href
40647      */   
40648     href : '',
40649     /**
40650      * @cfg {String} video
40651      */   
40652     video : '',
40653     /**
40654      * @cfg {Boolean} square
40655      */   
40656     square : true,
40657     
40658     getAutoCreate : function()
40659     {
40660         var cls = 'roo-brick';
40661         
40662         if(this.href.length){
40663             cls += ' roo-brick-link';
40664         }
40665         
40666         if(this.bgimage.length){
40667             cls += ' roo-brick-image';
40668         }
40669         
40670         if(!this.html.length && !this.bgimage.length){
40671             cls += ' roo-brick-center-title';
40672         }
40673         
40674         if(!this.html.length && this.bgimage.length){
40675             cls += ' roo-brick-bottom-title';
40676         }
40677         
40678         if(this.cls){
40679             cls += ' ' + this.cls;
40680         }
40681         
40682         var cfg = {
40683             tag: (this.href.length) ? 'a' : 'div',
40684             cls: cls,
40685             cn: [
40686                 {
40687                     tag: 'div',
40688                     cls: 'roo-brick-paragraph',
40689                     cn: []
40690                 }
40691             ]
40692         };
40693         
40694         if(this.href.length){
40695             cfg.href = this.href;
40696         }
40697         
40698         var cn = cfg.cn[0].cn;
40699         
40700         if(this.title.length){
40701             cn.push({
40702                 tag: 'h4',
40703                 cls: 'roo-brick-title',
40704                 html: this.title
40705             });
40706         }
40707         
40708         if(this.html.length){
40709             cn.push({
40710                 tag: 'p',
40711                 cls: 'roo-brick-text',
40712                 html: this.html
40713             });
40714         } else {
40715             cn.cls += ' hide';
40716         }
40717         
40718         if(this.bgimage.length){
40719             cfg.cn.push({
40720                 tag: 'img',
40721                 cls: 'roo-brick-image-view',
40722                 src: this.bgimage
40723             });
40724         }
40725         
40726         return cfg;
40727     },
40728     
40729     initEvents: function() 
40730     {
40731         if(this.title.length || this.html.length){
40732             this.el.on('mouseenter'  ,this.enter, this);
40733             this.el.on('mouseleave', this.leave, this);
40734         }
40735         
40736         Roo.EventManager.onWindowResize(this.resize, this); 
40737         
40738         if(this.bgimage.length){
40739             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40740             this.imageEl.on('load', this.onImageLoad, this);
40741             return;
40742         }
40743         
40744         this.resize();
40745     },
40746     
40747     onImageLoad : function()
40748     {
40749         this.resize();
40750     },
40751     
40752     resize : function()
40753     {
40754         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40755         
40756         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40757         
40758         if(this.bgimage.length){
40759             var image = this.el.select('.roo-brick-image-view', true).first();
40760             
40761             image.setWidth(paragraph.getWidth());
40762             
40763             if(this.square){
40764                 image.setHeight(paragraph.getWidth());
40765             }
40766             
40767             this.el.setHeight(image.getHeight());
40768             paragraph.setHeight(image.getHeight());
40769             
40770         }
40771         
40772     },
40773     
40774     enter: function(e, el)
40775     {
40776         e.preventDefault();
40777         
40778         if(this.bgimage.length){
40779             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40780             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40781         }
40782     },
40783     
40784     leave: function(e, el)
40785     {
40786         e.preventDefault();
40787         
40788         if(this.bgimage.length){
40789             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40790             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40791         }
40792     }
40793     
40794 });
40795
40796  
40797
40798  /*
40799  * - LGPL
40800  *
40801  * Number field 
40802  */
40803
40804 /**
40805  * @class Roo.bootstrap.form.NumberField
40806  * @extends Roo.bootstrap.form.Input
40807  * Bootstrap NumberField class
40808  * 
40809  * 
40810  * 
40811  * 
40812  * @constructor
40813  * Create a new NumberField
40814  * @param {Object} config The config object
40815  */
40816
40817 Roo.bootstrap.form.NumberField = function(config){
40818     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40819 };
40820
40821 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40822     
40823     /**
40824      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40825      */
40826     allowDecimals : true,
40827     /**
40828      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40829      */
40830     decimalSeparator : ".",
40831     /**
40832      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40833      */
40834     decimalPrecision : 2,
40835     /**
40836      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40837      */
40838     allowNegative : true,
40839     
40840     /**
40841      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40842      */
40843     allowZero: true,
40844     /**
40845      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40846      */
40847     minValue : Number.NEGATIVE_INFINITY,
40848     /**
40849      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40850      */
40851     maxValue : Number.MAX_VALUE,
40852     /**
40853      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40854      */
40855     minText : "The minimum value for this field is {0}",
40856     /**
40857      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40858      */
40859     maxText : "The maximum value for this field is {0}",
40860     /**
40861      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40862      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40863      */
40864     nanText : "{0} is not a valid number",
40865     /**
40866      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40867      */
40868     thousandsDelimiter : false,
40869     /**
40870      * @cfg {String} valueAlign alignment of value
40871      */
40872     valueAlign : "left",
40873
40874     getAutoCreate : function()
40875     {
40876         var hiddenInput = {
40877             tag: 'input',
40878             type: 'hidden',
40879             id: Roo.id(),
40880             cls: 'hidden-number-input'
40881         };
40882         
40883         if (this.name) {
40884             hiddenInput.name = this.name;
40885         }
40886         
40887         this.name = '';
40888         
40889         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40890         
40891         this.name = hiddenInput.name;
40892         
40893         if(cfg.cn.length > 0) {
40894             cfg.cn.push(hiddenInput);
40895         }
40896         
40897         return cfg;
40898     },
40899
40900     // private
40901     initEvents : function()
40902     {   
40903         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40904         
40905         var allowed = "0123456789";
40906         
40907         if(this.allowDecimals){
40908             allowed += this.decimalSeparator;
40909         }
40910         
40911         if(this.allowNegative){
40912             allowed += "-";
40913         }
40914         
40915         if(this.thousandsDelimiter) {
40916             allowed += ",";
40917         }
40918         
40919         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40920         
40921         var keyPress = function(e){
40922             
40923             var k = e.getKey();
40924             
40925             var c = e.getCharCode();
40926             
40927             if(
40928                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40929                     allowed.indexOf(String.fromCharCode(c)) === -1
40930             ){
40931                 e.stopEvent();
40932                 return;
40933             }
40934             
40935             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40936                 return;
40937             }
40938             
40939             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40940                 e.stopEvent();
40941             }
40942         };
40943         
40944         this.el.on("keypress", keyPress, this);
40945     },
40946     
40947     validateValue : function(value)
40948     {
40949         
40950         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40951             return false;
40952         }
40953         
40954         var num = this.parseValue(value);
40955         
40956         if(isNaN(num)){
40957             this.markInvalid(String.format(this.nanText, value));
40958             return false;
40959         }
40960         
40961         if(num < this.minValue){
40962             this.markInvalid(String.format(this.minText, this.minValue));
40963             return false;
40964         }
40965         
40966         if(num > this.maxValue){
40967             this.markInvalid(String.format(this.maxText, this.maxValue));
40968             return false;
40969         }
40970         
40971         return true;
40972     },
40973
40974     getValue : function()
40975     {
40976         var v = this.hiddenEl().getValue();
40977         
40978         return this.fixPrecision(this.parseValue(v));
40979     },
40980
40981     parseValue : function(value)
40982     {
40983         if(this.thousandsDelimiter) {
40984             value += "";
40985             r = new RegExp(",", "g");
40986             value = value.replace(r, "");
40987         }
40988         
40989         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40990         return isNaN(value) ? '' : value;
40991     },
40992
40993     fixPrecision : function(value)
40994     {
40995         if(this.thousandsDelimiter) {
40996             value += "";
40997             r = new RegExp(",", "g");
40998             value = value.replace(r, "");
40999         }
41000         
41001         var nan = isNaN(value);
41002         
41003         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41004             return nan ? '' : value;
41005         }
41006         return parseFloat(value).toFixed(this.decimalPrecision);
41007     },
41008
41009     setValue : function(v)
41010     {
41011         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41012         
41013         this.value = v;
41014         
41015         if(this.rendered){
41016             
41017             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41018             
41019             this.inputEl().dom.value = (v == '') ? '' :
41020                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41021             
41022             if(!this.allowZero && v === '0') {
41023                 this.hiddenEl().dom.value = '';
41024                 this.inputEl().dom.value = '';
41025             }
41026             
41027             this.validate();
41028         }
41029     },
41030
41031     decimalPrecisionFcn : function(v)
41032     {
41033         return Math.floor(v);
41034     },
41035
41036     beforeBlur : function()
41037     {
41038         var v = this.parseValue(this.getRawValue());
41039         
41040         if(v || v === 0 || v === ''){
41041             this.setValue(v);
41042         }
41043     },
41044     
41045     hiddenEl : function()
41046     {
41047         return this.el.select('input.hidden-number-input',true).first();
41048     }
41049     
41050 });
41051
41052  
41053
41054 /*
41055 * Licence: LGPL
41056 */
41057
41058 /**
41059  * @class Roo.bootstrap.DocumentSlider
41060  * @extends Roo.bootstrap.Component
41061  * Bootstrap DocumentSlider class
41062  * 
41063  * @constructor
41064  * Create a new DocumentViewer
41065  * @param {Object} config The config object
41066  */
41067
41068 Roo.bootstrap.DocumentSlider = function(config){
41069     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41070     
41071     this.files = [];
41072     
41073     this.addEvents({
41074         /**
41075          * @event initial
41076          * Fire after initEvent
41077          * @param {Roo.bootstrap.DocumentSlider} this
41078          */
41079         "initial" : true,
41080         /**
41081          * @event update
41082          * Fire after update
41083          * @param {Roo.bootstrap.DocumentSlider} this
41084          */
41085         "update" : true,
41086         /**
41087          * @event click
41088          * Fire after click
41089          * @param {Roo.bootstrap.DocumentSlider} this
41090          */
41091         "click" : true
41092     });
41093 };
41094
41095 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41096     
41097     files : false,
41098     
41099     indicator : 0,
41100     
41101     getAutoCreate : function()
41102     {
41103         var cfg = {
41104             tag : 'div',
41105             cls : 'roo-document-slider',
41106             cn : [
41107                 {
41108                     tag : 'div',
41109                     cls : 'roo-document-slider-header',
41110                     cn : [
41111                         {
41112                             tag : 'div',
41113                             cls : 'roo-document-slider-header-title'
41114                         }
41115                     ]
41116                 },
41117                 {
41118                     tag : 'div',
41119                     cls : 'roo-document-slider-body',
41120                     cn : [
41121                         {
41122                             tag : 'div',
41123                             cls : 'roo-document-slider-prev',
41124                             cn : [
41125                                 {
41126                                     tag : 'i',
41127                                     cls : 'fa fa-chevron-left'
41128                                 }
41129                             ]
41130                         },
41131                         {
41132                             tag : 'div',
41133                             cls : 'roo-document-slider-thumb',
41134                             cn : [
41135                                 {
41136                                     tag : 'img',
41137                                     cls : 'roo-document-slider-image'
41138                                 }
41139                             ]
41140                         },
41141                         {
41142                             tag : 'div',
41143                             cls : 'roo-document-slider-next',
41144                             cn : [
41145                                 {
41146                                     tag : 'i',
41147                                     cls : 'fa fa-chevron-right'
41148                                 }
41149                             ]
41150                         }
41151                     ]
41152                 }
41153             ]
41154         };
41155         
41156         return cfg;
41157     },
41158     
41159     initEvents : function()
41160     {
41161         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41162         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41163         
41164         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41165         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41166         
41167         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41168         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41169         
41170         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41171         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41172         
41173         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41174         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41175         
41176         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41177         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41178         
41179         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41180         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41181         
41182         this.thumbEl.on('click', this.onClick, this);
41183         
41184         this.prevIndicator.on('click', this.prev, this);
41185         
41186         this.nextIndicator.on('click', this.next, this);
41187         
41188     },
41189     
41190     initial : function()
41191     {
41192         if(this.files.length){
41193             this.indicator = 1;
41194             this.update()
41195         }
41196         
41197         this.fireEvent('initial', this);
41198     },
41199     
41200     update : function()
41201     {
41202         this.imageEl.attr('src', this.files[this.indicator - 1]);
41203         
41204         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41205         
41206         this.prevIndicator.show();
41207         
41208         if(this.indicator == 1){
41209             this.prevIndicator.hide();
41210         }
41211         
41212         this.nextIndicator.show();
41213         
41214         if(this.indicator == this.files.length){
41215             this.nextIndicator.hide();
41216         }
41217         
41218         this.thumbEl.scrollTo('top');
41219         
41220         this.fireEvent('update', this);
41221     },
41222     
41223     onClick : function(e)
41224     {
41225         e.preventDefault();
41226         
41227         this.fireEvent('click', this);
41228     },
41229     
41230     prev : function(e)
41231     {
41232         e.preventDefault();
41233         
41234         this.indicator = Math.max(1, this.indicator - 1);
41235         
41236         this.update();
41237     },
41238     
41239     next : function(e)
41240     {
41241         e.preventDefault();
41242         
41243         this.indicator = Math.min(this.files.length, this.indicator + 1);
41244         
41245         this.update();
41246     }
41247 });
41248 /*
41249  * - LGPL
41250  *
41251  * RadioSet
41252  *
41253  *
41254  */
41255
41256 /**
41257  * @class Roo.bootstrap.form.RadioSet
41258  * @extends Roo.bootstrap.form.Input
41259  * @children Roo.bootstrap.form.Radio
41260  * Bootstrap RadioSet class
41261  * @cfg {String} indicatorpos (left|right) default left
41262  * @cfg {Boolean} inline (true|false) inline the element (default true)
41263  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41264  * @constructor
41265  * Create a new RadioSet
41266  * @param {Object} config The config object
41267  */
41268
41269 Roo.bootstrap.form.RadioSet = function(config){
41270     
41271     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41272     
41273     this.radioes = [];
41274     
41275     Roo.bootstrap.form.RadioSet.register(this);
41276     
41277     this.addEvents({
41278         /**
41279         * @event check
41280         * Fires when the element is checked or unchecked.
41281         * @param {Roo.bootstrap.form.RadioSet} this This radio
41282         * @param {Roo.bootstrap.form.Radio} item The checked item
41283         */
41284        check : true,
41285        /**
41286         * @event click
41287         * Fires when the element is click.
41288         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41289         * @param {Roo.bootstrap.form.Radio} item The checked item
41290         * @param {Roo.EventObject} e The event object
41291         */
41292        click : true
41293     });
41294     
41295 };
41296
41297 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41298
41299     radioes : false,
41300     
41301     inline : true,
41302     
41303     weight : '',
41304     
41305     indicatorpos : 'left',
41306     
41307     getAutoCreate : function()
41308     {
41309         var label = {
41310             tag : 'label',
41311             cls : 'roo-radio-set-label',
41312             cn : [
41313                 {
41314                     tag : 'span',
41315                     html : this.fieldLabel
41316                 }
41317             ]
41318         };
41319         if (Roo.bootstrap.version == 3) {
41320             
41321             
41322             if(this.indicatorpos == 'left'){
41323                 label.cn.unshift({
41324                     tag : 'i',
41325                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41326                     tooltip : 'This field is required'
41327                 });
41328             } else {
41329                 label.cn.push({
41330                     tag : 'i',
41331                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41332                     tooltip : 'This field is required'
41333                 });
41334             }
41335         }
41336         var items = {
41337             tag : 'div',
41338             cls : 'roo-radio-set-items'
41339         };
41340         
41341         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41342         
41343         if (align === 'left' && this.fieldLabel.length) {
41344             
41345             items = {
41346                 cls : "roo-radio-set-right", 
41347                 cn: [
41348                     items
41349                 ]
41350             };
41351             
41352             if(this.labelWidth > 12){
41353                 label.style = "width: " + this.labelWidth + 'px';
41354             }
41355             
41356             if(this.labelWidth < 13 && this.labelmd == 0){
41357                 this.labelmd = this.labelWidth;
41358             }
41359             
41360             if(this.labellg > 0){
41361                 label.cls += ' col-lg-' + this.labellg;
41362                 items.cls += ' col-lg-' + (12 - this.labellg);
41363             }
41364             
41365             if(this.labelmd > 0){
41366                 label.cls += ' col-md-' + this.labelmd;
41367                 items.cls += ' col-md-' + (12 - this.labelmd);
41368             }
41369             
41370             if(this.labelsm > 0){
41371                 label.cls += ' col-sm-' + this.labelsm;
41372                 items.cls += ' col-sm-' + (12 - this.labelsm);
41373             }
41374             
41375             if(this.labelxs > 0){
41376                 label.cls += ' col-xs-' + this.labelxs;
41377                 items.cls += ' col-xs-' + (12 - this.labelxs);
41378             }
41379         }
41380         
41381         var cfg = {
41382             tag : 'div',
41383             cls : 'roo-radio-set',
41384             cn : [
41385                 {
41386                     tag : 'input',
41387                     cls : 'roo-radio-set-input',
41388                     type : 'hidden',
41389                     name : this.name,
41390                     value : this.value ? this.value :  ''
41391                 },
41392                 label,
41393                 items
41394             ]
41395         };
41396         
41397         if(this.weight.length){
41398             cfg.cls += ' roo-radio-' + this.weight;
41399         }
41400         
41401         if(this.inline) {
41402             cfg.cls += ' roo-radio-set-inline';
41403         }
41404         
41405         var settings=this;
41406         ['xs','sm','md','lg'].map(function(size){
41407             if (settings[size]) {
41408                 cfg.cls += ' col-' + size + '-' + settings[size];
41409             }
41410         });
41411         
41412         return cfg;
41413         
41414     },
41415
41416     initEvents : function()
41417     {
41418         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41419         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41420         
41421         if(!this.fieldLabel.length){
41422             this.labelEl.hide();
41423         }
41424         
41425         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41426         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41427         
41428         this.indicator = this.indicatorEl();
41429         
41430         if(this.indicator){
41431             this.indicator.addClass('invisible');
41432         }
41433         
41434         this.originalValue = this.getValue();
41435         
41436     },
41437     
41438     inputEl: function ()
41439     {
41440         return this.el.select('.roo-radio-set-input', true).first();
41441     },
41442     
41443     getChildContainer : function()
41444     {
41445         return this.itemsEl;
41446     },
41447     
41448     register : function(item)
41449     {
41450         this.radioes.push(item);
41451         
41452     },
41453     
41454     validate : function()
41455     {   
41456         if(this.getVisibilityEl().hasClass('hidden')){
41457             return true;
41458         }
41459         
41460         var valid = false;
41461         
41462         Roo.each(this.radioes, function(i){
41463             if(!i.checked){
41464                 return;
41465             }
41466             
41467             valid = true;
41468             return false;
41469         });
41470         
41471         if(this.allowBlank) {
41472             return true;
41473         }
41474         
41475         if(this.disabled || valid){
41476             this.markValid();
41477             return true;
41478         }
41479         
41480         this.markInvalid();
41481         return false;
41482         
41483     },
41484     
41485     markValid : function()
41486     {
41487         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41488             this.indicatorEl().removeClass('visible');
41489             this.indicatorEl().addClass('invisible');
41490         }
41491         
41492         
41493         if (Roo.bootstrap.version == 3) {
41494             this.el.removeClass([this.invalidClass, this.validClass]);
41495             this.el.addClass(this.validClass);
41496         } else {
41497             this.el.removeClass(['is-invalid','is-valid']);
41498             this.el.addClass(['is-valid']);
41499         }
41500         this.fireEvent('valid', this);
41501     },
41502     
41503     markInvalid : function(msg)
41504     {
41505         if(this.allowBlank || this.disabled){
41506             return;
41507         }
41508         
41509         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41510             this.indicatorEl().removeClass('invisible');
41511             this.indicatorEl().addClass('visible');
41512         }
41513         if (Roo.bootstrap.version == 3) {
41514             this.el.removeClass([this.invalidClass, this.validClass]);
41515             this.el.addClass(this.invalidClass);
41516         } else {
41517             this.el.removeClass(['is-invalid','is-valid']);
41518             this.el.addClass(['is-invalid']);
41519         }
41520         
41521         this.fireEvent('invalid', this, msg);
41522         
41523     },
41524     
41525     setValue : function(v, suppressEvent)
41526     {   
41527         if(this.value === v){
41528             return;
41529         }
41530         
41531         this.value = v;
41532         
41533         if(this.rendered){
41534             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41535         }
41536         
41537         Roo.each(this.radioes, function(i){
41538             i.checked = false;
41539             i.el.removeClass('checked');
41540         });
41541         
41542         Roo.each(this.radioes, function(i){
41543             
41544             if(i.value === v || i.value.toString() === v.toString()){
41545                 i.checked = true;
41546                 i.el.addClass('checked');
41547                 
41548                 if(suppressEvent !== true){
41549                     this.fireEvent('check', this, i);
41550                 }
41551                 
41552                 return false;
41553             }
41554             
41555         }, this);
41556         
41557         this.validate();
41558     },
41559     
41560     clearInvalid : function(){
41561         
41562         if(!this.el || this.preventMark){
41563             return;
41564         }
41565         
41566         this.el.removeClass([this.invalidClass]);
41567         
41568         this.fireEvent('valid', this);
41569     }
41570     
41571 });
41572
41573 Roo.apply(Roo.bootstrap.form.RadioSet, {
41574     
41575     groups: {},
41576     
41577     register : function(set)
41578     {
41579         this.groups[set.name] = set;
41580     },
41581     
41582     get: function(name) 
41583     {
41584         if (typeof(this.groups[name]) == 'undefined') {
41585             return false;
41586         }
41587         
41588         return this.groups[name] ;
41589     }
41590     
41591 });
41592 /*
41593  * Based on:
41594  * Ext JS Library 1.1.1
41595  * Copyright(c) 2006-2007, Ext JS, LLC.
41596  *
41597  * Originally Released Under LGPL - original licence link has changed is not relivant.
41598  *
41599  * Fork - LGPL
41600  * <script type="text/javascript">
41601  */
41602
41603
41604 /**
41605  * @class Roo.bootstrap.SplitBar
41606  * @extends Roo.util.Observable
41607  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41608  * <br><br>
41609  * Usage:
41610  * <pre><code>
41611 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41612                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41613 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41614 split.minSize = 100;
41615 split.maxSize = 600;
41616 split.animate = true;
41617 split.on('moved', splitterMoved);
41618 </code></pre>
41619  * @constructor
41620  * Create a new SplitBar
41621  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41622  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41623  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41624  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41625                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41626                         position of the SplitBar).
41627  */
41628 Roo.bootstrap.SplitBar = function(cfg){
41629     
41630     /** @private */
41631     
41632     //{
41633     //  dragElement : elm
41634     //  resizingElement: el,
41635         // optional..
41636     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41637     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41638         // existingProxy ???
41639     //}
41640     
41641     this.el = Roo.get(cfg.dragElement, true);
41642     this.el.dom.unselectable = "on";
41643     /** @private */
41644     this.resizingEl = Roo.get(cfg.resizingElement, true);
41645
41646     /**
41647      * @private
41648      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41649      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41650      * @type Number
41651      */
41652     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41653     
41654     /**
41655      * The minimum size of the resizing element. (Defaults to 0)
41656      * @type Number
41657      */
41658     this.minSize = 0;
41659     
41660     /**
41661      * The maximum size of the resizing element. (Defaults to 2000)
41662      * @type Number
41663      */
41664     this.maxSize = 2000;
41665     
41666     /**
41667      * Whether to animate the transition to the new size
41668      * @type Boolean
41669      */
41670     this.animate = false;
41671     
41672     /**
41673      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41674      * @type Boolean
41675      */
41676     this.useShim = false;
41677     
41678     /** @private */
41679     this.shim = null;
41680     
41681     if(!cfg.existingProxy){
41682         /** @private */
41683         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41684     }else{
41685         this.proxy = Roo.get(cfg.existingProxy).dom;
41686     }
41687     /** @private */
41688     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41689     
41690     /** @private */
41691     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41692     
41693     /** @private */
41694     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41695     
41696     /** @private */
41697     this.dragSpecs = {};
41698     
41699     /**
41700      * @private The adapter to use to positon and resize elements
41701      */
41702     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41703     this.adapter.init(this);
41704     
41705     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41706         /** @private */
41707         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41708         this.el.addClass("roo-splitbar-h");
41709     }else{
41710         /** @private */
41711         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41712         this.el.addClass("roo-splitbar-v");
41713     }
41714     
41715     this.addEvents({
41716         /**
41717          * @event resize
41718          * Fires when the splitter is moved (alias for {@link #event-moved})
41719          * @param {Roo.bootstrap.SplitBar} this
41720          * @param {Number} newSize the new width or height
41721          */
41722         "resize" : true,
41723         /**
41724          * @event moved
41725          * Fires when the splitter is moved
41726          * @param {Roo.bootstrap.SplitBar} this
41727          * @param {Number} newSize the new width or height
41728          */
41729         "moved" : true,
41730         /**
41731          * @event beforeresize
41732          * Fires before the splitter is dragged
41733          * @param {Roo.bootstrap.SplitBar} this
41734          */
41735         "beforeresize" : true,
41736
41737         "beforeapply" : true
41738     });
41739
41740     Roo.util.Observable.call(this);
41741 };
41742
41743 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41744     onStartProxyDrag : function(x, y){
41745         this.fireEvent("beforeresize", this);
41746         if(!this.overlay){
41747             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41748             o.unselectable();
41749             o.enableDisplayMode("block");
41750             // all splitbars share the same overlay
41751             Roo.bootstrap.SplitBar.prototype.overlay = o;
41752         }
41753         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41754         this.overlay.show();
41755         Roo.get(this.proxy).setDisplayed("block");
41756         var size = this.adapter.getElementSize(this);
41757         this.activeMinSize = this.getMinimumSize();;
41758         this.activeMaxSize = this.getMaximumSize();;
41759         var c1 = size - this.activeMinSize;
41760         var c2 = Math.max(this.activeMaxSize - size, 0);
41761         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41762             this.dd.resetConstraints();
41763             this.dd.setXConstraint(
41764                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41765                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41766             );
41767             this.dd.setYConstraint(0, 0);
41768         }else{
41769             this.dd.resetConstraints();
41770             this.dd.setXConstraint(0, 0);
41771             this.dd.setYConstraint(
41772                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41773                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41774             );
41775          }
41776         this.dragSpecs.startSize = size;
41777         this.dragSpecs.startPoint = [x, y];
41778         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41779     },
41780     
41781     /** 
41782      * @private Called after the drag operation by the DDProxy
41783      */
41784     onEndProxyDrag : function(e){
41785         Roo.get(this.proxy).setDisplayed(false);
41786         var endPoint = Roo.lib.Event.getXY(e);
41787         if(this.overlay){
41788             this.overlay.hide();
41789         }
41790         var newSize;
41791         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41792             newSize = this.dragSpecs.startSize + 
41793                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41794                     endPoint[0] - this.dragSpecs.startPoint[0] :
41795                     this.dragSpecs.startPoint[0] - endPoint[0]
41796                 );
41797         }else{
41798             newSize = this.dragSpecs.startSize + 
41799                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41800                     endPoint[1] - this.dragSpecs.startPoint[1] :
41801                     this.dragSpecs.startPoint[1] - endPoint[1]
41802                 );
41803         }
41804         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41805         if(newSize != this.dragSpecs.startSize){
41806             if(this.fireEvent('beforeapply', this, newSize) !== false){
41807                 this.adapter.setElementSize(this, newSize);
41808                 this.fireEvent("moved", this, newSize);
41809                 this.fireEvent("resize", this, newSize);
41810             }
41811         }
41812     },
41813     
41814     /**
41815      * Get the adapter this SplitBar uses
41816      * @return The adapter object
41817      */
41818     getAdapter : function(){
41819         return this.adapter;
41820     },
41821     
41822     /**
41823      * Set the adapter this SplitBar uses
41824      * @param {Object} adapter A SplitBar adapter object
41825      */
41826     setAdapter : function(adapter){
41827         this.adapter = adapter;
41828         this.adapter.init(this);
41829     },
41830     
41831     /**
41832      * Gets the minimum size for the resizing element
41833      * @return {Number} The minimum size
41834      */
41835     getMinimumSize : function(){
41836         return this.minSize;
41837     },
41838     
41839     /**
41840      * Sets the minimum size for the resizing element
41841      * @param {Number} minSize The minimum size
41842      */
41843     setMinimumSize : function(minSize){
41844         this.minSize = minSize;
41845     },
41846     
41847     /**
41848      * Gets the maximum size for the resizing element
41849      * @return {Number} The maximum size
41850      */
41851     getMaximumSize : function(){
41852         return this.maxSize;
41853     },
41854     
41855     /**
41856      * Sets the maximum size for the resizing element
41857      * @param {Number} maxSize The maximum size
41858      */
41859     setMaximumSize : function(maxSize){
41860         this.maxSize = maxSize;
41861     },
41862     
41863     /**
41864      * Sets the initialize size for the resizing element
41865      * @param {Number} size The initial size
41866      */
41867     setCurrentSize : function(size){
41868         var oldAnimate = this.animate;
41869         this.animate = false;
41870         this.adapter.setElementSize(this, size);
41871         this.animate = oldAnimate;
41872     },
41873     
41874     /**
41875      * Destroy this splitbar. 
41876      * @param {Boolean} removeEl True to remove the element
41877      */
41878     destroy : function(removeEl){
41879         if(this.shim){
41880             this.shim.remove();
41881         }
41882         this.dd.unreg();
41883         this.proxy.parentNode.removeChild(this.proxy);
41884         if(removeEl){
41885             this.el.remove();
41886         }
41887     }
41888 });
41889
41890 /**
41891  * @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.
41892  */
41893 Roo.bootstrap.SplitBar.createProxy = function(dir){
41894     var proxy = new Roo.Element(document.createElement("div"));
41895     proxy.unselectable();
41896     var cls = 'roo-splitbar-proxy';
41897     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41898     document.body.appendChild(proxy.dom);
41899     return proxy.dom;
41900 };
41901
41902 /** 
41903  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41904  * Default Adapter. It assumes the splitter and resizing element are not positioned
41905  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41906  */
41907 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41908 };
41909
41910 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41911     // do nothing for now
41912     init : function(s){
41913     
41914     },
41915     /**
41916      * Called before drag operations to get the current size of the resizing element. 
41917      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41918      */
41919      getElementSize : function(s){
41920         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41921             return s.resizingEl.getWidth();
41922         }else{
41923             return s.resizingEl.getHeight();
41924         }
41925     },
41926     
41927     /**
41928      * Called after drag operations to set the size of the resizing element.
41929      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41930      * @param {Number} newSize The new size to set
41931      * @param {Function} onComplete A function to be invoked when resizing is complete
41932      */
41933     setElementSize : function(s, newSize, onComplete){
41934         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41935             if(!s.animate){
41936                 s.resizingEl.setWidth(newSize);
41937                 if(onComplete){
41938                     onComplete(s, newSize);
41939                 }
41940             }else{
41941                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41942             }
41943         }else{
41944             
41945             if(!s.animate){
41946                 s.resizingEl.setHeight(newSize);
41947                 if(onComplete){
41948                     onComplete(s, newSize);
41949                 }
41950             }else{
41951                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41952             }
41953         }
41954     }
41955 };
41956
41957 /** 
41958  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41959  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41960  * Adapter that  moves the splitter element to align with the resized sizing element. 
41961  * Used with an absolute positioned SplitBar.
41962  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41963  * document.body, make sure you assign an id to the body element.
41964  */
41965 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41966     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41967     this.container = Roo.get(container);
41968 };
41969
41970 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41971     init : function(s){
41972         this.basic.init(s);
41973     },
41974     
41975     getElementSize : function(s){
41976         return this.basic.getElementSize(s);
41977     },
41978     
41979     setElementSize : function(s, newSize, onComplete){
41980         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41981     },
41982     
41983     moveSplitter : function(s){
41984         var yes = Roo.bootstrap.SplitBar;
41985         switch(s.placement){
41986             case yes.LEFT:
41987                 s.el.setX(s.resizingEl.getRight());
41988                 break;
41989             case yes.RIGHT:
41990                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41991                 break;
41992             case yes.TOP:
41993                 s.el.setY(s.resizingEl.getBottom());
41994                 break;
41995             case yes.BOTTOM:
41996                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41997                 break;
41998         }
41999     }
42000 };
42001
42002 /**
42003  * Orientation constant - Create a vertical SplitBar
42004  * @static
42005  * @type Number
42006  */
42007 Roo.bootstrap.SplitBar.VERTICAL = 1;
42008
42009 /**
42010  * Orientation constant - Create a horizontal SplitBar
42011  * @static
42012  * @type Number
42013  */
42014 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42015
42016 /**
42017  * Placement constant - The resizing element is to the left of the splitter element
42018  * @static
42019  * @type Number
42020  */
42021 Roo.bootstrap.SplitBar.LEFT = 1;
42022
42023 /**
42024  * Placement constant - The resizing element is to the right of the splitter element
42025  * @static
42026  * @type Number
42027  */
42028 Roo.bootstrap.SplitBar.RIGHT = 2;
42029
42030 /**
42031  * Placement constant - The resizing element is positioned above the splitter element
42032  * @static
42033  * @type Number
42034  */
42035 Roo.bootstrap.SplitBar.TOP = 3;
42036
42037 /**
42038  * Placement constant - The resizing element is positioned under splitter element
42039  * @static
42040  * @type Number
42041  */
42042 Roo.bootstrap.SplitBar.BOTTOM = 4;
42043 /*
42044  * Based on:
42045  * Ext JS Library 1.1.1
42046  * Copyright(c) 2006-2007, Ext JS, LLC.
42047  *
42048  * Originally Released Under LGPL - original licence link has changed is not relivant.
42049  *
42050  * Fork - LGPL
42051  * <script type="text/javascript">
42052  */
42053
42054 /**
42055  * @class Roo.bootstrap.layout.Manager
42056  * @extends Roo.bootstrap.Component
42057  * @abstract
42058  * Base class for layout managers.
42059  */
42060 Roo.bootstrap.layout.Manager = function(config)
42061 {
42062     this.monitorWindowResize = true; // do this before we apply configuration.
42063     
42064     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42065
42066
42067
42068
42069
42070     /** false to disable window resize monitoring @type Boolean */
42071     
42072     this.regions = {};
42073     this.addEvents({
42074         /**
42075          * @event layout
42076          * Fires when a layout is performed.
42077          * @param {Roo.LayoutManager} this
42078          */
42079         "layout" : true,
42080         /**
42081          * @event regionresized
42082          * Fires when the user resizes a region.
42083          * @param {Roo.LayoutRegion} region The resized region
42084          * @param {Number} newSize The new size (width for east/west, height for north/south)
42085          */
42086         "regionresized" : true,
42087         /**
42088          * @event regioncollapsed
42089          * Fires when a region is collapsed.
42090          * @param {Roo.LayoutRegion} region The collapsed region
42091          */
42092         "regioncollapsed" : true,
42093         /**
42094          * @event regionexpanded
42095          * Fires when a region is expanded.
42096          * @param {Roo.LayoutRegion} region The expanded region
42097          */
42098         "regionexpanded" : true
42099     });
42100     this.updating = false;
42101
42102     if (config.el) {
42103         this.el = Roo.get(config.el);
42104         this.initEvents();
42105     }
42106
42107 };
42108
42109 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42110
42111
42112     regions : null,
42113
42114     monitorWindowResize : true,
42115
42116
42117     updating : false,
42118
42119
42120     onRender : function(ct, position)
42121     {
42122         if(!this.el){
42123             this.el = Roo.get(ct);
42124             this.initEvents();
42125         }
42126         //this.fireEvent('render',this);
42127     },
42128
42129
42130     initEvents: function()
42131     {
42132
42133
42134         // ie scrollbar fix
42135         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42136             document.body.scroll = "no";
42137         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42138             this.el.position('relative');
42139         }
42140         this.id = this.el.id;
42141         this.el.addClass("roo-layout-container");
42142         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42143         if(this.el.dom != document.body ) {
42144             this.el.on('resize', this.layout,this);
42145             this.el.on('show', this.layout,this);
42146         }
42147
42148     },
42149
42150     /**
42151      * Returns true if this layout is currently being updated
42152      * @return {Boolean}
42153      */
42154     isUpdating : function(){
42155         return this.updating;
42156     },
42157
42158     /**
42159      * Suspend the LayoutManager from doing auto-layouts while
42160      * making multiple add or remove calls
42161      */
42162     beginUpdate : function(){
42163         this.updating = true;
42164     },
42165
42166     /**
42167      * Restore auto-layouts and optionally disable the manager from performing a layout
42168      * @param {Boolean} noLayout true to disable a layout update
42169      */
42170     endUpdate : function(noLayout){
42171         this.updating = false;
42172         if(!noLayout){
42173             this.layout();
42174         }
42175     },
42176
42177     layout: function(){
42178         // abstract...
42179     },
42180
42181     onRegionResized : function(region, newSize){
42182         this.fireEvent("regionresized", region, newSize);
42183         this.layout();
42184     },
42185
42186     onRegionCollapsed : function(region){
42187         this.fireEvent("regioncollapsed", region);
42188     },
42189
42190     onRegionExpanded : function(region){
42191         this.fireEvent("regionexpanded", region);
42192     },
42193
42194     /**
42195      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42196      * performs box-model adjustments.
42197      * @return {Object} The size as an object {width: (the width), height: (the height)}
42198      */
42199     getViewSize : function()
42200     {
42201         var size;
42202         if(this.el.dom != document.body){
42203             size = this.el.getSize();
42204         }else{
42205             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42206         }
42207         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42208         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42209         return size;
42210     },
42211
42212     /**
42213      * Returns the Element this layout is bound to.
42214      * @return {Roo.Element}
42215      */
42216     getEl : function(){
42217         return this.el;
42218     },
42219
42220     /**
42221      * Returns the specified region.
42222      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42223      * @return {Roo.LayoutRegion}
42224      */
42225     getRegion : function(target){
42226         return this.regions[target.toLowerCase()];
42227     },
42228
42229     onWindowResize : function(){
42230         if(this.monitorWindowResize){
42231             this.layout();
42232         }
42233     }
42234 });
42235 /*
42236  * Based on:
42237  * Ext JS Library 1.1.1
42238  * Copyright(c) 2006-2007, Ext JS, LLC.
42239  *
42240  * Originally Released Under LGPL - original licence link has changed is not relivant.
42241  *
42242  * Fork - LGPL
42243  * <script type="text/javascript">
42244  */
42245 /**
42246  * @class Roo.bootstrap.layout.Border
42247  * @extends Roo.bootstrap.layout.Manager
42248  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42249  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42250  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42251  * please see: examples/bootstrap/nested.html<br><br>
42252  
42253 <b>The container the layout is rendered into can be either the body element or any other element.
42254 If it is not the body element, the container needs to either be an absolute positioned element,
42255 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42256 the container size if it is not the body element.</b>
42257
42258 * @constructor
42259 * Create a new Border
42260 * @param {Object} config Configuration options
42261  */
42262 Roo.bootstrap.layout.Border = function(config){
42263     config = config || {};
42264     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42265     
42266     
42267     
42268     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42269         if(config[region]){
42270             config[region].region = region;
42271             this.addRegion(config[region]);
42272         }
42273     },this);
42274     
42275 };
42276
42277 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42278
42279 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42280     
42281         /**
42282          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42283          */
42284         /**
42285          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42286          */
42287         /**
42288          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42289          */
42290         /**
42291          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42292          */
42293         /**
42294          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42295          */
42296         
42297         
42298         
42299         
42300     parent : false, // this might point to a 'nest' or a ???
42301     
42302     /**
42303      * Creates and adds a new region if it doesn't already exist.
42304      * @param {String} target The target region key (north, south, east, west or center).
42305      * @param {Object} config The regions config object
42306      * @return {BorderLayoutRegion} The new region
42307      */
42308     addRegion : function(config)
42309     {
42310         if(!this.regions[config.region]){
42311             var r = this.factory(config);
42312             this.bindRegion(r);
42313         }
42314         return this.regions[config.region];
42315     },
42316
42317     // private (kinda)
42318     bindRegion : function(r){
42319         this.regions[r.config.region] = r;
42320         
42321         r.on("visibilitychange",    this.layout, this);
42322         r.on("paneladded",          this.layout, this);
42323         r.on("panelremoved",        this.layout, this);
42324         r.on("invalidated",         this.layout, this);
42325         r.on("resized",             this.onRegionResized, this);
42326         r.on("collapsed",           this.onRegionCollapsed, this);
42327         r.on("expanded",            this.onRegionExpanded, this);
42328     },
42329
42330     /**
42331      * Performs a layout update.
42332      */
42333     layout : function()
42334     {
42335         if(this.updating) {
42336             return;
42337         }
42338         
42339         // render all the rebions if they have not been done alreayd?
42340         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42341             if(this.regions[region] && !this.regions[region].bodyEl){
42342                 this.regions[region].onRender(this.el)
42343             }
42344         },this);
42345         
42346         var size = this.getViewSize();
42347         var w = size.width;
42348         var h = size.height;
42349         var centerW = w;
42350         var centerH = h;
42351         var centerY = 0;
42352         var centerX = 0;
42353         //var x = 0, y = 0;
42354
42355         var rs = this.regions;
42356         var north = rs["north"];
42357         var south = rs["south"]; 
42358         var west = rs["west"];
42359         var east = rs["east"];
42360         var center = rs["center"];
42361         //if(this.hideOnLayout){ // not supported anymore
42362             //c.el.setStyle("display", "none");
42363         //}
42364         if(north && north.isVisible()){
42365             var b = north.getBox();
42366             var m = north.getMargins();
42367             b.width = w - (m.left+m.right);
42368             b.x = m.left;
42369             b.y = m.top;
42370             centerY = b.height + b.y + m.bottom;
42371             centerH -= centerY;
42372             north.updateBox(this.safeBox(b));
42373         }
42374         if(south && south.isVisible()){
42375             var b = south.getBox();
42376             var m = south.getMargins();
42377             b.width = w - (m.left+m.right);
42378             b.x = m.left;
42379             var totalHeight = (b.height + m.top + m.bottom);
42380             b.y = h - totalHeight + m.top;
42381             centerH -= totalHeight;
42382             south.updateBox(this.safeBox(b));
42383         }
42384         if(west && west.isVisible()){
42385             var b = west.getBox();
42386             var m = west.getMargins();
42387             b.height = centerH - (m.top+m.bottom);
42388             b.x = m.left;
42389             b.y = centerY + m.top;
42390             var totalWidth = (b.width + m.left + m.right);
42391             centerX += totalWidth;
42392             centerW -= totalWidth;
42393             west.updateBox(this.safeBox(b));
42394         }
42395         if(east && east.isVisible()){
42396             var b = east.getBox();
42397             var m = east.getMargins();
42398             b.height = centerH - (m.top+m.bottom);
42399             var totalWidth = (b.width + m.left + m.right);
42400             b.x = w - totalWidth + m.left;
42401             b.y = centerY + m.top;
42402             centerW -= totalWidth;
42403             east.updateBox(this.safeBox(b));
42404         }
42405         if(center){
42406             var m = center.getMargins();
42407             var centerBox = {
42408                 x: centerX + m.left,
42409                 y: centerY + m.top,
42410                 width: centerW - (m.left+m.right),
42411                 height: centerH - (m.top+m.bottom)
42412             };
42413             //if(this.hideOnLayout){
42414                 //center.el.setStyle("display", "block");
42415             //}
42416             center.updateBox(this.safeBox(centerBox));
42417         }
42418         this.el.repaint();
42419         this.fireEvent("layout", this);
42420     },
42421
42422     // private
42423     safeBox : function(box){
42424         box.width = Math.max(0, box.width);
42425         box.height = Math.max(0, box.height);
42426         return box;
42427     },
42428
42429     /**
42430      * Adds a ContentPanel (or subclass) to this layout.
42431      * @param {String} target The target region key (north, south, east, west or center).
42432      * @param {Roo.ContentPanel} panel The panel to add
42433      * @return {Roo.ContentPanel} The added panel
42434      */
42435     add : function(target, panel){
42436          
42437         target = target.toLowerCase();
42438         return this.regions[target].add(panel);
42439     },
42440
42441     /**
42442      * Remove a ContentPanel (or subclass) to this layout.
42443      * @param {String} target The target region key (north, south, east, west or center).
42444      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42445      * @return {Roo.ContentPanel} The removed panel
42446      */
42447     remove : function(target, panel){
42448         target = target.toLowerCase();
42449         return this.regions[target].remove(panel);
42450     },
42451
42452     /**
42453      * Searches all regions for a panel with the specified id
42454      * @param {String} panelId
42455      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42456      */
42457     findPanel : function(panelId){
42458         var rs = this.regions;
42459         for(var target in rs){
42460             if(typeof rs[target] != "function"){
42461                 var p = rs[target].getPanel(panelId);
42462                 if(p){
42463                     return p;
42464                 }
42465             }
42466         }
42467         return null;
42468     },
42469
42470     /**
42471      * Searches all regions for a panel with the specified id and activates (shows) it.
42472      * @param {String/ContentPanel} panelId The panels id or the panel itself
42473      * @return {Roo.ContentPanel} The shown panel or null
42474      */
42475     showPanel : function(panelId) {
42476       var rs = this.regions;
42477       for(var target in rs){
42478          var r = rs[target];
42479          if(typeof r != "function"){
42480             if(r.hasPanel(panelId)){
42481                return r.showPanel(panelId);
42482             }
42483          }
42484       }
42485       return null;
42486    },
42487
42488    /**
42489      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42490      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42491      */
42492    /*
42493     restoreState : function(provider){
42494         if(!provider){
42495             provider = Roo.state.Manager;
42496         }
42497         var sm = new Roo.LayoutStateManager();
42498         sm.init(this, provider);
42499     },
42500 */
42501  
42502  
42503     /**
42504      * Adds a xtype elements to the layout.
42505      * <pre><code>
42506
42507 layout.addxtype({
42508        xtype : 'ContentPanel',
42509        region: 'west',
42510        items: [ .... ]
42511    }
42512 );
42513
42514 layout.addxtype({
42515         xtype : 'NestedLayoutPanel',
42516         region: 'west',
42517         layout: {
42518            center: { },
42519            west: { }   
42520         },
42521         items : [ ... list of content panels or nested layout panels.. ]
42522    }
42523 );
42524 </code></pre>
42525      * @param {Object} cfg Xtype definition of item to add.
42526      */
42527     addxtype : function(cfg)
42528     {
42529         // basically accepts a pannel...
42530         // can accept a layout region..!?!?
42531         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42532         
42533         
42534         // theory?  children can only be panels??
42535         
42536         //if (!cfg.xtype.match(/Panel$/)) {
42537         //    return false;
42538         //}
42539         var ret = false;
42540         
42541         if (typeof(cfg.region) == 'undefined') {
42542             Roo.log("Failed to add Panel, region was not set");
42543             Roo.log(cfg);
42544             return false;
42545         }
42546         var region = cfg.region;
42547         delete cfg.region;
42548         
42549           
42550         var xitems = [];
42551         if (cfg.items) {
42552             xitems = cfg.items;
42553             delete cfg.items;
42554         }
42555         var nb = false;
42556         
42557         if ( region == 'center') {
42558             Roo.log("Center: " + cfg.title);
42559         }
42560         
42561         
42562         switch(cfg.xtype) 
42563         {
42564             case 'Content':  // ContentPanel (el, cfg)
42565             case 'Scroll':  // ContentPanel (el, cfg)
42566             case 'View': 
42567                 cfg.autoCreate = cfg.autoCreate || true;
42568                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42569                 //} else {
42570                 //    var el = this.el.createChild();
42571                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42572                 //}
42573                 
42574                 this.add(region, ret);
42575                 break;
42576             
42577             /*
42578             case 'TreePanel': // our new panel!
42579                 cfg.el = this.el.createChild();
42580                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42581                 this.add(region, ret);
42582                 break;
42583             */
42584             
42585             case 'Nest': 
42586                 // create a new Layout (which is  a Border Layout...
42587                 
42588                 var clayout = cfg.layout;
42589                 clayout.el  = this.el.createChild();
42590                 clayout.items   = clayout.items  || [];
42591                 
42592                 delete cfg.layout;
42593                 
42594                 // replace this exitems with the clayout ones..
42595                 xitems = clayout.items;
42596                  
42597                 // force background off if it's in center...
42598                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42599                     cfg.background = false;
42600                 }
42601                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42602                 
42603                 
42604                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42605                 //console.log('adding nested layout panel '  + cfg.toSource());
42606                 this.add(region, ret);
42607                 nb = {}; /// find first...
42608                 break;
42609             
42610             case 'Grid':
42611                 
42612                 // needs grid and region
42613                 
42614                 //var el = this.getRegion(region).el.createChild();
42615                 /*
42616                  *var el = this.el.createChild();
42617                 // create the grid first...
42618                 cfg.grid.container = el;
42619                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42620                 */
42621                 
42622                 if (region == 'center' && this.active ) {
42623                     cfg.background = false;
42624                 }
42625                 
42626                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42627                 
42628                 this.add(region, ret);
42629                 /*
42630                 if (cfg.background) {
42631                     // render grid on panel activation (if panel background)
42632                     ret.on('activate', function(gp) {
42633                         if (!gp.grid.rendered) {
42634                     //        gp.grid.render(el);
42635                         }
42636                     });
42637                 } else {
42638                   //  cfg.grid.render(el);
42639                 }
42640                 */
42641                 break;
42642            
42643            
42644             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42645                 // it was the old xcomponent building that caused this before.
42646                 // espeically if border is the top element in the tree.
42647                 ret = this;
42648                 break; 
42649                 
42650                     
42651                 
42652                 
42653                 
42654             default:
42655                 /*
42656                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42657                     
42658                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42659                     this.add(region, ret);
42660                 } else {
42661                 */
42662                     Roo.log(cfg);
42663                     throw "Can not add '" + cfg.xtype + "' to Border";
42664                     return null;
42665              
42666                                 
42667              
42668         }
42669         this.beginUpdate();
42670         // add children..
42671         var region = '';
42672         var abn = {};
42673         Roo.each(xitems, function(i)  {
42674             region = nb && i.region ? i.region : false;
42675             
42676             var add = ret.addxtype(i);
42677            
42678             if (region) {
42679                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42680                 if (!i.background) {
42681                     abn[region] = nb[region] ;
42682                 }
42683             }
42684             
42685         });
42686         this.endUpdate();
42687
42688         // make the last non-background panel active..
42689         //if (nb) { Roo.log(abn); }
42690         if (nb) {
42691             
42692             for(var r in abn) {
42693                 region = this.getRegion(r);
42694                 if (region) {
42695                     // tried using nb[r], but it does not work..
42696                      
42697                     region.showPanel(abn[r]);
42698                    
42699                 }
42700             }
42701         }
42702         return ret;
42703         
42704     },
42705     
42706     
42707 // private
42708     factory : function(cfg)
42709     {
42710         
42711         var validRegions = Roo.bootstrap.layout.Border.regions;
42712
42713         var target = cfg.region;
42714         cfg.mgr = this;
42715         
42716         var r = Roo.bootstrap.layout;
42717         Roo.log(target);
42718         switch(target){
42719             case "north":
42720                 return new r.North(cfg);
42721             case "south":
42722                 return new r.South(cfg);
42723             case "east":
42724                 return new r.East(cfg);
42725             case "west":
42726                 return new r.West(cfg);
42727             case "center":
42728                 return new r.Center(cfg);
42729         }
42730         throw 'Layout region "'+target+'" not supported.';
42731     }
42732     
42733     
42734 });
42735  /*
42736  * Based on:
42737  * Ext JS Library 1.1.1
42738  * Copyright(c) 2006-2007, Ext JS, LLC.
42739  *
42740  * Originally Released Under LGPL - original licence link has changed is not relivant.
42741  *
42742  * Fork - LGPL
42743  * <script type="text/javascript">
42744  */
42745  
42746 /**
42747  * @class Roo.bootstrap.layout.Basic
42748  * @extends Roo.util.Observable
42749  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42750  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42751  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42752  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42753  * @cfg {string}   region  the region that it inhabits..
42754  * @cfg {bool}   skipConfig skip config?
42755  * 
42756
42757  */
42758 Roo.bootstrap.layout.Basic = function(config){
42759     
42760     this.mgr = config.mgr;
42761     
42762     this.position = config.region;
42763     
42764     var skipConfig = config.skipConfig;
42765     
42766     this.events = {
42767         /**
42768          * @scope Roo.BasicLayoutRegion
42769          */
42770         
42771         /**
42772          * @event beforeremove
42773          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42774          * @param {Roo.LayoutRegion} this
42775          * @param {Roo.ContentPanel} panel The panel
42776          * @param {Object} e The cancel event object
42777          */
42778         "beforeremove" : true,
42779         /**
42780          * @event invalidated
42781          * Fires when the layout for this region is changed.
42782          * @param {Roo.LayoutRegion} this
42783          */
42784         "invalidated" : true,
42785         /**
42786          * @event visibilitychange
42787          * Fires when this region is shown or hidden 
42788          * @param {Roo.LayoutRegion} this
42789          * @param {Boolean} visibility true or false
42790          */
42791         "visibilitychange" : true,
42792         /**
42793          * @event paneladded
42794          * Fires when a panel is added. 
42795          * @param {Roo.LayoutRegion} this
42796          * @param {Roo.ContentPanel} panel The panel
42797          */
42798         "paneladded" : true,
42799         /**
42800          * @event panelremoved
42801          * Fires when a panel is removed. 
42802          * @param {Roo.LayoutRegion} this
42803          * @param {Roo.ContentPanel} panel The panel
42804          */
42805         "panelremoved" : true,
42806         /**
42807          * @event beforecollapse
42808          * Fires when this region before collapse.
42809          * @param {Roo.LayoutRegion} this
42810          */
42811         "beforecollapse" : true,
42812         /**
42813          * @event collapsed
42814          * Fires when this region is collapsed.
42815          * @param {Roo.LayoutRegion} this
42816          */
42817         "collapsed" : true,
42818         /**
42819          * @event expanded
42820          * Fires when this region is expanded.
42821          * @param {Roo.LayoutRegion} this
42822          */
42823         "expanded" : true,
42824         /**
42825          * @event slideshow
42826          * Fires when this region is slid into view.
42827          * @param {Roo.LayoutRegion} this
42828          */
42829         "slideshow" : true,
42830         /**
42831          * @event slidehide
42832          * Fires when this region slides out of view. 
42833          * @param {Roo.LayoutRegion} this
42834          */
42835         "slidehide" : true,
42836         /**
42837          * @event panelactivated
42838          * Fires when a panel is activated. 
42839          * @param {Roo.LayoutRegion} this
42840          * @param {Roo.ContentPanel} panel The activated panel
42841          */
42842         "panelactivated" : true,
42843         /**
42844          * @event resized
42845          * Fires when the user resizes this region. 
42846          * @param {Roo.LayoutRegion} this
42847          * @param {Number} newSize The new size (width for east/west, height for north/south)
42848          */
42849         "resized" : true
42850     };
42851     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42852     this.panels = new Roo.util.MixedCollection();
42853     this.panels.getKey = this.getPanelId.createDelegate(this);
42854     this.box = null;
42855     this.activePanel = null;
42856     // ensure listeners are added...
42857     
42858     if (config.listeners || config.events) {
42859         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42860             listeners : config.listeners || {},
42861             events : config.events || {}
42862         });
42863     }
42864     
42865     if(skipConfig !== true){
42866         this.applyConfig(config);
42867     }
42868 };
42869
42870 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42871 {
42872     getPanelId : function(p){
42873         return p.getId();
42874     },
42875     
42876     applyConfig : function(config){
42877         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42878         this.config = config;
42879         
42880     },
42881     
42882     /**
42883      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42884      * the width, for horizontal (north, south) the height.
42885      * @param {Number} newSize The new width or height
42886      */
42887     resizeTo : function(newSize){
42888         var el = this.el ? this.el :
42889                  (this.activePanel ? this.activePanel.getEl() : null);
42890         if(el){
42891             switch(this.position){
42892                 case "east":
42893                 case "west":
42894                     el.setWidth(newSize);
42895                     this.fireEvent("resized", this, newSize);
42896                 break;
42897                 case "north":
42898                 case "south":
42899                     el.setHeight(newSize);
42900                     this.fireEvent("resized", this, newSize);
42901                 break;                
42902             }
42903         }
42904     },
42905     
42906     getBox : function(){
42907         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42908     },
42909     
42910     getMargins : function(){
42911         return this.margins;
42912     },
42913     
42914     updateBox : function(box){
42915         this.box = box;
42916         var el = this.activePanel.getEl();
42917         el.dom.style.left = box.x + "px";
42918         el.dom.style.top = box.y + "px";
42919         this.activePanel.setSize(box.width, box.height);
42920     },
42921     
42922     /**
42923      * Returns the container element for this region.
42924      * @return {Roo.Element}
42925      */
42926     getEl : function(){
42927         return this.activePanel;
42928     },
42929     
42930     /**
42931      * Returns true if this region is currently visible.
42932      * @return {Boolean}
42933      */
42934     isVisible : function(){
42935         return this.activePanel ? true : false;
42936     },
42937     
42938     setActivePanel : function(panel){
42939         panel = this.getPanel(panel);
42940         if(this.activePanel && this.activePanel != panel){
42941             this.activePanel.setActiveState(false);
42942             this.activePanel.getEl().setLeftTop(-10000,-10000);
42943         }
42944         this.activePanel = panel;
42945         panel.setActiveState(true);
42946         if(this.box){
42947             panel.setSize(this.box.width, this.box.height);
42948         }
42949         this.fireEvent("panelactivated", this, panel);
42950         this.fireEvent("invalidated");
42951     },
42952     
42953     /**
42954      * Show the specified panel.
42955      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42956      * @return {Roo.ContentPanel} The shown panel or null
42957      */
42958     showPanel : function(panel){
42959         panel = this.getPanel(panel);
42960         if(panel){
42961             this.setActivePanel(panel);
42962         }
42963         return panel;
42964     },
42965     
42966     /**
42967      * Get the active panel for this region.
42968      * @return {Roo.ContentPanel} The active panel or null
42969      */
42970     getActivePanel : function(){
42971         return this.activePanel;
42972     },
42973     
42974     /**
42975      * Add the passed ContentPanel(s)
42976      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42977      * @return {Roo.ContentPanel} The panel added (if only one was added)
42978      */
42979     add : function(panel){
42980         if(arguments.length > 1){
42981             for(var i = 0, len = arguments.length; i < len; i++) {
42982                 this.add(arguments[i]);
42983             }
42984             return null;
42985         }
42986         if(this.hasPanel(panel)){
42987             this.showPanel(panel);
42988             return panel;
42989         }
42990         var el = panel.getEl();
42991         if(el.dom.parentNode != this.mgr.el.dom){
42992             this.mgr.el.dom.appendChild(el.dom);
42993         }
42994         if(panel.setRegion){
42995             panel.setRegion(this);
42996         }
42997         this.panels.add(panel);
42998         el.setStyle("position", "absolute");
42999         if(!panel.background){
43000             this.setActivePanel(panel);
43001             if(this.config.initialSize && this.panels.getCount()==1){
43002                 this.resizeTo(this.config.initialSize);
43003             }
43004         }
43005         this.fireEvent("paneladded", this, panel);
43006         return panel;
43007     },
43008     
43009     /**
43010      * Returns true if the panel is in this region.
43011      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43012      * @return {Boolean}
43013      */
43014     hasPanel : function(panel){
43015         if(typeof panel == "object"){ // must be panel obj
43016             panel = panel.getId();
43017         }
43018         return this.getPanel(panel) ? true : false;
43019     },
43020     
43021     /**
43022      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43023      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43024      * @param {Boolean} preservePanel Overrides the config preservePanel option
43025      * @return {Roo.ContentPanel} The panel that was removed
43026      */
43027     remove : function(panel, preservePanel){
43028         panel = this.getPanel(panel);
43029         if(!panel){
43030             return null;
43031         }
43032         var e = {};
43033         this.fireEvent("beforeremove", this, panel, e);
43034         if(e.cancel === true){
43035             return null;
43036         }
43037         var panelId = panel.getId();
43038         this.panels.removeKey(panelId);
43039         return panel;
43040     },
43041     
43042     /**
43043      * Returns the panel specified or null if it's not in this region.
43044      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43045      * @return {Roo.ContentPanel}
43046      */
43047     getPanel : function(id){
43048         if(typeof id == "object"){ // must be panel obj
43049             return id;
43050         }
43051         return this.panels.get(id);
43052     },
43053     
43054     /**
43055      * Returns this regions position (north/south/east/west/center).
43056      * @return {String} 
43057      */
43058     getPosition: function(){
43059         return this.position;    
43060     }
43061 });/*
43062  * Based on:
43063  * Ext JS Library 1.1.1
43064  * Copyright(c) 2006-2007, Ext JS, LLC.
43065  *
43066  * Originally Released Under LGPL - original licence link has changed is not relivant.
43067  *
43068  * Fork - LGPL
43069  * <script type="text/javascript">
43070  */
43071  
43072 /**
43073  * @class Roo.bootstrap.layout.Region
43074  * @extends Roo.bootstrap.layout.Basic
43075  * This class represents a region in a layout manager.
43076  
43077  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43078  * @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})
43079  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43080  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43081  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43082  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43083  * @cfg {String}    title           The title for the region (overrides panel titles)
43084  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43085  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43086  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43087  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43088  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43089  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43090  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43091  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43092  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43093  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43094
43095  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43096  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43097  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43098  * @cfg {Number}    width           For East/West panels
43099  * @cfg {Number}    height          For North/South panels
43100  * @cfg {Boolean}   split           To show the splitter
43101  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43102  * 
43103  * @cfg {string}   cls             Extra CSS classes to add to region
43104  * 
43105  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43106  * @cfg {string}   region  the region that it inhabits..
43107  *
43108
43109  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43110  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43111
43112  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43113  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43114  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43115  */
43116 Roo.bootstrap.layout.Region = function(config)
43117 {
43118     this.applyConfig(config);
43119
43120     var mgr = config.mgr;
43121     var pos = config.region;
43122     config.skipConfig = true;
43123     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43124     
43125     if (mgr.el) {
43126         this.onRender(mgr.el);   
43127     }
43128      
43129     this.visible = true;
43130     this.collapsed = false;
43131     this.unrendered_panels = [];
43132 };
43133
43134 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43135
43136     position: '', // set by wrapper (eg. north/south etc..)
43137     unrendered_panels : null,  // unrendered panels.
43138     
43139     tabPosition : false,
43140     
43141     mgr: false, // points to 'Border'
43142     
43143     
43144     createBody : function(){
43145         /** This region's body element 
43146         * @type Roo.Element */
43147         this.bodyEl = this.el.createChild({
43148                 tag: "div",
43149                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43150         });
43151     },
43152
43153     onRender: function(ctr, pos)
43154     {
43155         var dh = Roo.DomHelper;
43156         /** This region's container element 
43157         * @type Roo.Element */
43158         this.el = dh.append(ctr.dom, {
43159                 tag: "div",
43160                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43161             }, true);
43162         /** This region's title element 
43163         * @type Roo.Element */
43164     
43165         this.titleEl = dh.append(this.el.dom,  {
43166                 tag: "div",
43167                 unselectable: "on",
43168                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43169                 children:[
43170                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43171                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43172                 ]
43173             }, true);
43174         
43175         this.titleEl.enableDisplayMode();
43176         /** This region's title text element 
43177         * @type HTMLElement */
43178         this.titleTextEl = this.titleEl.dom.firstChild;
43179         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43180         /*
43181         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43182         this.closeBtn.enableDisplayMode();
43183         this.closeBtn.on("click", this.closeClicked, this);
43184         this.closeBtn.hide();
43185     */
43186         this.createBody(this.config);
43187         if(this.config.hideWhenEmpty){
43188             this.hide();
43189             this.on("paneladded", this.validateVisibility, this);
43190             this.on("panelremoved", this.validateVisibility, this);
43191         }
43192         if(this.autoScroll){
43193             this.bodyEl.setStyle("overflow", "auto");
43194         }else{
43195             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43196         }
43197         //if(c.titlebar !== false){
43198             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43199                 this.titleEl.hide();
43200             }else{
43201                 this.titleEl.show();
43202                 if(this.config.title){
43203                     this.titleTextEl.innerHTML = this.config.title;
43204                 }
43205             }
43206         //}
43207         if(this.config.collapsed){
43208             this.collapse(true);
43209         }
43210         if(this.config.hidden){
43211             this.hide();
43212         }
43213         
43214         if (this.unrendered_panels && this.unrendered_panels.length) {
43215             for (var i =0;i< this.unrendered_panels.length; i++) {
43216                 this.add(this.unrendered_panels[i]);
43217             }
43218             this.unrendered_panels = null;
43219             
43220         }
43221         
43222     },
43223     
43224     applyConfig : function(c)
43225     {
43226         /*
43227          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43228             var dh = Roo.DomHelper;
43229             if(c.titlebar !== false){
43230                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43231                 this.collapseBtn.on("click", this.collapse, this);
43232                 this.collapseBtn.enableDisplayMode();
43233                 /*
43234                 if(c.showPin === true || this.showPin){
43235                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43236                     this.stickBtn.enableDisplayMode();
43237                     this.stickBtn.on("click", this.expand, this);
43238                     this.stickBtn.hide();
43239                 }
43240                 
43241             }
43242             */
43243             /** This region's collapsed element
43244             * @type Roo.Element */
43245             /*
43246              *
43247             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43248                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43249             ]}, true);
43250             
43251             if(c.floatable !== false){
43252                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43253                this.collapsedEl.on("click", this.collapseClick, this);
43254             }
43255
43256             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43257                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43258                    id: "message", unselectable: "on", style:{"float":"left"}});
43259                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43260              }
43261             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43262             this.expandBtn.on("click", this.expand, this);
43263             
43264         }
43265         
43266         if(this.collapseBtn){
43267             this.collapseBtn.setVisible(c.collapsible == true);
43268         }
43269         
43270         this.cmargins = c.cmargins || this.cmargins ||
43271                          (this.position == "west" || this.position == "east" ?
43272                              {top: 0, left: 2, right:2, bottom: 0} :
43273                              {top: 2, left: 0, right:0, bottom: 2});
43274         */
43275         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43276         
43277         
43278         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43279         
43280         this.autoScroll = c.autoScroll || false;
43281         
43282         
43283        
43284         
43285         this.duration = c.duration || .30;
43286         this.slideDuration = c.slideDuration || .45;
43287         this.config = c;
43288        
43289     },
43290     /**
43291      * Returns true if this region is currently visible.
43292      * @return {Boolean}
43293      */
43294     isVisible : function(){
43295         return this.visible;
43296     },
43297
43298     /**
43299      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43300      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43301      */
43302     //setCollapsedTitle : function(title){
43303     //    title = title || "&#160;";
43304      //   if(this.collapsedTitleTextEl){
43305       //      this.collapsedTitleTextEl.innerHTML = title;
43306        // }
43307     //},
43308
43309     getBox : function(){
43310         var b;
43311       //  if(!this.collapsed){
43312             b = this.el.getBox(false, true);
43313        // }else{
43314           //  b = this.collapsedEl.getBox(false, true);
43315         //}
43316         return b;
43317     },
43318
43319     getMargins : function(){
43320         return this.margins;
43321         //return this.collapsed ? this.cmargins : this.margins;
43322     },
43323 /*
43324     highlight : function(){
43325         this.el.addClass("x-layout-panel-dragover");
43326     },
43327
43328     unhighlight : function(){
43329         this.el.removeClass("x-layout-panel-dragover");
43330     },
43331 */
43332     updateBox : function(box)
43333     {
43334         if (!this.bodyEl) {
43335             return; // not rendered yet..
43336         }
43337         
43338         this.box = box;
43339         if(!this.collapsed){
43340             this.el.dom.style.left = box.x + "px";
43341             this.el.dom.style.top = box.y + "px";
43342             this.updateBody(box.width, box.height);
43343         }else{
43344             this.collapsedEl.dom.style.left = box.x + "px";
43345             this.collapsedEl.dom.style.top = box.y + "px";
43346             this.collapsedEl.setSize(box.width, box.height);
43347         }
43348         if(this.tabs){
43349             this.tabs.autoSizeTabs();
43350         }
43351     },
43352
43353     updateBody : function(w, h)
43354     {
43355         if(w !== null){
43356             this.el.setWidth(w);
43357             w -= this.el.getBorderWidth("rl");
43358             if(this.config.adjustments){
43359                 w += this.config.adjustments[0];
43360             }
43361         }
43362         if(h !== null && h > 0){
43363             this.el.setHeight(h);
43364             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43365             h -= this.el.getBorderWidth("tb");
43366             if(this.config.adjustments){
43367                 h += this.config.adjustments[1];
43368             }
43369             this.bodyEl.setHeight(h);
43370             if(this.tabs){
43371                 h = this.tabs.syncHeight(h);
43372             }
43373         }
43374         if(this.panelSize){
43375             w = w !== null ? w : this.panelSize.width;
43376             h = h !== null ? h : this.panelSize.height;
43377         }
43378         if(this.activePanel){
43379             var el = this.activePanel.getEl();
43380             w = w !== null ? w : el.getWidth();
43381             h = h !== null ? h : el.getHeight();
43382             this.panelSize = {width: w, height: h};
43383             this.activePanel.setSize(w, h);
43384         }
43385         if(Roo.isIE && this.tabs){
43386             this.tabs.el.repaint();
43387         }
43388     },
43389
43390     /**
43391      * Returns the container element for this region.
43392      * @return {Roo.Element}
43393      */
43394     getEl : function(){
43395         return this.el;
43396     },
43397
43398     /**
43399      * Hides this region.
43400      */
43401     hide : function(){
43402         //if(!this.collapsed){
43403             this.el.dom.style.left = "-2000px";
43404             this.el.hide();
43405         //}else{
43406          //   this.collapsedEl.dom.style.left = "-2000px";
43407          //   this.collapsedEl.hide();
43408        // }
43409         this.visible = false;
43410         this.fireEvent("visibilitychange", this, false);
43411     },
43412
43413     /**
43414      * Shows this region if it was previously hidden.
43415      */
43416     show : function(){
43417         //if(!this.collapsed){
43418             this.el.show();
43419         //}else{
43420         //    this.collapsedEl.show();
43421        // }
43422         this.visible = true;
43423         this.fireEvent("visibilitychange", this, true);
43424     },
43425 /*
43426     closeClicked : function(){
43427         if(this.activePanel){
43428             this.remove(this.activePanel);
43429         }
43430     },
43431
43432     collapseClick : function(e){
43433         if(this.isSlid){
43434            e.stopPropagation();
43435            this.slideIn();
43436         }else{
43437            e.stopPropagation();
43438            this.slideOut();
43439         }
43440     },
43441 */
43442     /**
43443      * Collapses this region.
43444      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43445      */
43446     /*
43447     collapse : function(skipAnim, skipCheck = false){
43448         if(this.collapsed) {
43449             return;
43450         }
43451         
43452         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43453             
43454             this.collapsed = true;
43455             if(this.split){
43456                 this.split.el.hide();
43457             }
43458             if(this.config.animate && skipAnim !== true){
43459                 this.fireEvent("invalidated", this);
43460                 this.animateCollapse();
43461             }else{
43462                 this.el.setLocation(-20000,-20000);
43463                 this.el.hide();
43464                 this.collapsedEl.show();
43465                 this.fireEvent("collapsed", this);
43466                 this.fireEvent("invalidated", this);
43467             }
43468         }
43469         
43470     },
43471 */
43472     animateCollapse : function(){
43473         // overridden
43474     },
43475
43476     /**
43477      * Expands this region if it was previously collapsed.
43478      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43479      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43480      */
43481     /*
43482     expand : function(e, skipAnim){
43483         if(e) {
43484             e.stopPropagation();
43485         }
43486         if(!this.collapsed || this.el.hasActiveFx()) {
43487             return;
43488         }
43489         if(this.isSlid){
43490             this.afterSlideIn();
43491             skipAnim = true;
43492         }
43493         this.collapsed = false;
43494         if(this.config.animate && skipAnim !== true){
43495             this.animateExpand();
43496         }else{
43497             this.el.show();
43498             if(this.split){
43499                 this.split.el.show();
43500             }
43501             this.collapsedEl.setLocation(-2000,-2000);
43502             this.collapsedEl.hide();
43503             this.fireEvent("invalidated", this);
43504             this.fireEvent("expanded", this);
43505         }
43506     },
43507 */
43508     animateExpand : function(){
43509         // overridden
43510     },
43511
43512     initTabs : function()
43513     {
43514         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43515         
43516         var ts = new Roo.bootstrap.panel.Tabs({
43517             el: this.bodyEl.dom,
43518             region : this,
43519             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43520             disableTooltips: this.config.disableTabTips,
43521             toolbar : this.config.toolbar
43522         });
43523         
43524         if(this.config.hideTabs){
43525             ts.stripWrap.setDisplayed(false);
43526         }
43527         this.tabs = ts;
43528         ts.resizeTabs = this.config.resizeTabs === true;
43529         ts.minTabWidth = this.config.minTabWidth || 40;
43530         ts.maxTabWidth = this.config.maxTabWidth || 250;
43531         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43532         ts.monitorResize = false;
43533         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43534         ts.bodyEl.addClass('roo-layout-tabs-body');
43535         this.panels.each(this.initPanelAsTab, this);
43536     },
43537
43538     initPanelAsTab : function(panel){
43539         var ti = this.tabs.addTab(
43540             panel.getEl().id,
43541             panel.getTitle(),
43542             null,
43543             this.config.closeOnTab && panel.isClosable(),
43544             panel.tpl
43545         );
43546         if(panel.tabTip !== undefined){
43547             ti.setTooltip(panel.tabTip);
43548         }
43549         ti.on("activate", function(){
43550               this.setActivePanel(panel);
43551         }, this);
43552         
43553         if(this.config.closeOnTab){
43554             ti.on("beforeclose", function(t, e){
43555                 e.cancel = true;
43556                 this.remove(panel);
43557             }, this);
43558         }
43559         
43560         panel.tabItem = ti;
43561         
43562         return ti;
43563     },
43564
43565     updatePanelTitle : function(panel, title)
43566     {
43567         if(this.activePanel == panel){
43568             this.updateTitle(title);
43569         }
43570         if(this.tabs){
43571             var ti = this.tabs.getTab(panel.getEl().id);
43572             ti.setText(title);
43573             if(panel.tabTip !== undefined){
43574                 ti.setTooltip(panel.tabTip);
43575             }
43576         }
43577     },
43578
43579     updateTitle : function(title){
43580         if(this.titleTextEl && !this.config.title){
43581             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43582         }
43583     },
43584
43585     setActivePanel : function(panel)
43586     {
43587         panel = this.getPanel(panel);
43588         if(this.activePanel && this.activePanel != panel){
43589             if(this.activePanel.setActiveState(false) === false){
43590                 return;
43591             }
43592         }
43593         this.activePanel = panel;
43594         panel.setActiveState(true);
43595         if(this.panelSize){
43596             panel.setSize(this.panelSize.width, this.panelSize.height);
43597         }
43598         if(this.closeBtn){
43599             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43600         }
43601         this.updateTitle(panel.getTitle());
43602         if(this.tabs){
43603             this.fireEvent("invalidated", this);
43604         }
43605         this.fireEvent("panelactivated", this, panel);
43606     },
43607
43608     /**
43609      * Shows the specified panel.
43610      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43611      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43612      */
43613     showPanel : function(panel)
43614     {
43615         panel = this.getPanel(panel);
43616         if(panel){
43617             if(this.tabs){
43618                 var tab = this.tabs.getTab(panel.getEl().id);
43619                 if(tab.isHidden()){
43620                     this.tabs.unhideTab(tab.id);
43621                 }
43622                 tab.activate();
43623             }else{
43624                 this.setActivePanel(panel);
43625             }
43626         }
43627         return panel;
43628     },
43629
43630     /**
43631      * Get the active panel for this region.
43632      * @return {Roo.ContentPanel} The active panel or null
43633      */
43634     getActivePanel : function(){
43635         return this.activePanel;
43636     },
43637
43638     validateVisibility : function(){
43639         if(this.panels.getCount() < 1){
43640             this.updateTitle("&#160;");
43641             this.closeBtn.hide();
43642             this.hide();
43643         }else{
43644             if(!this.isVisible()){
43645                 this.show();
43646             }
43647         }
43648     },
43649
43650     /**
43651      * Adds the passed ContentPanel(s) to this region.
43652      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43653      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43654      */
43655     add : function(panel)
43656     {
43657         if(arguments.length > 1){
43658             for(var i = 0, len = arguments.length; i < len; i++) {
43659                 this.add(arguments[i]);
43660             }
43661             return null;
43662         }
43663         
43664         // if we have not been rendered yet, then we can not really do much of this..
43665         if (!this.bodyEl) {
43666             this.unrendered_panels.push(panel);
43667             return panel;
43668         }
43669         
43670         
43671         
43672         
43673         if(this.hasPanel(panel)){
43674             this.showPanel(panel);
43675             return panel;
43676         }
43677         panel.setRegion(this);
43678         this.panels.add(panel);
43679        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43680             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43681             // and hide them... ???
43682             this.bodyEl.dom.appendChild(panel.getEl().dom);
43683             if(panel.background !== true){
43684                 this.setActivePanel(panel);
43685             }
43686             this.fireEvent("paneladded", this, panel);
43687             return panel;
43688         }
43689         */
43690         if(!this.tabs){
43691             this.initTabs();
43692         }else{
43693             this.initPanelAsTab(panel);
43694         }
43695         
43696         
43697         if(panel.background !== true){
43698             this.tabs.activate(panel.getEl().id);
43699         }
43700         this.fireEvent("paneladded", this, panel);
43701         return panel;
43702     },
43703
43704     /**
43705      * Hides the tab for the specified panel.
43706      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43707      */
43708     hidePanel : function(panel){
43709         if(this.tabs && (panel = this.getPanel(panel))){
43710             this.tabs.hideTab(panel.getEl().id);
43711         }
43712     },
43713
43714     /**
43715      * Unhides the tab for a previously hidden panel.
43716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43717      */
43718     unhidePanel : function(panel){
43719         if(this.tabs && (panel = this.getPanel(panel))){
43720             this.tabs.unhideTab(panel.getEl().id);
43721         }
43722     },
43723
43724     clearPanels : function(){
43725         while(this.panels.getCount() > 0){
43726              this.remove(this.panels.first());
43727         }
43728     },
43729
43730     /**
43731      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43732      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43733      * @param {Boolean} preservePanel Overrides the config preservePanel option
43734      * @return {Roo.ContentPanel} The panel that was removed
43735      */
43736     remove : function(panel, preservePanel)
43737     {
43738         panel = this.getPanel(panel);
43739         if(!panel){
43740             return null;
43741         }
43742         var e = {};
43743         this.fireEvent("beforeremove", this, panel, e);
43744         if(e.cancel === true){
43745             return null;
43746         }
43747         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43748         var panelId = panel.getId();
43749         this.panels.removeKey(panelId);
43750         if(preservePanel){
43751             document.body.appendChild(panel.getEl().dom);
43752         }
43753         if(this.tabs){
43754             this.tabs.removeTab(panel.getEl().id);
43755         }else if (!preservePanel){
43756             this.bodyEl.dom.removeChild(panel.getEl().dom);
43757         }
43758         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43759             var p = this.panels.first();
43760             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43761             tempEl.appendChild(p.getEl().dom);
43762             this.bodyEl.update("");
43763             this.bodyEl.dom.appendChild(p.getEl().dom);
43764             tempEl = null;
43765             this.updateTitle(p.getTitle());
43766             this.tabs = null;
43767             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43768             this.setActivePanel(p);
43769         }
43770         panel.setRegion(null);
43771         if(this.activePanel == panel){
43772             this.activePanel = null;
43773         }
43774         if(this.config.autoDestroy !== false && preservePanel !== true){
43775             try{panel.destroy();}catch(e){}
43776         }
43777         this.fireEvent("panelremoved", this, panel);
43778         return panel;
43779     },
43780
43781     /**
43782      * Returns the TabPanel component used by this region
43783      * @return {Roo.TabPanel}
43784      */
43785     getTabs : function(){
43786         return this.tabs;
43787     },
43788
43789     createTool : function(parentEl, className){
43790         var btn = Roo.DomHelper.append(parentEl, {
43791             tag: "div",
43792             cls: "x-layout-tools-button",
43793             children: [ {
43794                 tag: "div",
43795                 cls: "roo-layout-tools-button-inner " + className,
43796                 html: "&#160;"
43797             }]
43798         }, true);
43799         btn.addClassOnOver("roo-layout-tools-button-over");
43800         return btn;
43801     }
43802 });/*
43803  * Based on:
43804  * Ext JS Library 1.1.1
43805  * Copyright(c) 2006-2007, Ext JS, LLC.
43806  *
43807  * Originally Released Under LGPL - original licence link has changed is not relivant.
43808  *
43809  * Fork - LGPL
43810  * <script type="text/javascript">
43811  */
43812  
43813
43814
43815 /**
43816  * @class Roo.SplitLayoutRegion
43817  * @extends Roo.LayoutRegion
43818  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43819  */
43820 Roo.bootstrap.layout.Split = function(config){
43821     this.cursor = config.cursor;
43822     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43823 };
43824
43825 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43826 {
43827     splitTip : "Drag to resize.",
43828     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43829     useSplitTips : false,
43830
43831     applyConfig : function(config){
43832         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43833     },
43834     
43835     onRender : function(ctr,pos) {
43836         
43837         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43838         if(!this.config.split){
43839             return;
43840         }
43841         if(!this.split){
43842             
43843             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43844                             tag: "div",
43845                             id: this.el.id + "-split",
43846                             cls: "roo-layout-split roo-layout-split-"+this.position,
43847                             html: "&#160;"
43848             });
43849             /** The SplitBar for this region 
43850             * @type Roo.SplitBar */
43851             // does not exist yet...
43852             Roo.log([this.position, this.orientation]);
43853             
43854             this.split = new Roo.bootstrap.SplitBar({
43855                 dragElement : splitEl,
43856                 resizingElement: this.el,
43857                 orientation : this.orientation
43858             });
43859             
43860             this.split.on("moved", this.onSplitMove, this);
43861             this.split.useShim = this.config.useShim === true;
43862             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43863             if(this.useSplitTips){
43864                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43865             }
43866             //if(config.collapsible){
43867             //    this.split.el.on("dblclick", this.collapse,  this);
43868             //}
43869         }
43870         if(typeof this.config.minSize != "undefined"){
43871             this.split.minSize = this.config.minSize;
43872         }
43873         if(typeof this.config.maxSize != "undefined"){
43874             this.split.maxSize = this.config.maxSize;
43875         }
43876         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43877             this.hideSplitter();
43878         }
43879         
43880     },
43881
43882     getHMaxSize : function(){
43883          var cmax = this.config.maxSize || 10000;
43884          var center = this.mgr.getRegion("center");
43885          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43886     },
43887
43888     getVMaxSize : function(){
43889          var cmax = this.config.maxSize || 10000;
43890          var center = this.mgr.getRegion("center");
43891          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43892     },
43893
43894     onSplitMove : function(split, newSize){
43895         this.fireEvent("resized", this, newSize);
43896     },
43897     
43898     /** 
43899      * Returns the {@link Roo.SplitBar} for this region.
43900      * @return {Roo.SplitBar}
43901      */
43902     getSplitBar : function(){
43903         return this.split;
43904     },
43905     
43906     hide : function(){
43907         this.hideSplitter();
43908         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43909     },
43910
43911     hideSplitter : function(){
43912         if(this.split){
43913             this.split.el.setLocation(-2000,-2000);
43914             this.split.el.hide();
43915         }
43916     },
43917
43918     show : function(){
43919         if(this.split){
43920             this.split.el.show();
43921         }
43922         Roo.bootstrap.layout.Split.superclass.show.call(this);
43923     },
43924     
43925     beforeSlide: function(){
43926         if(Roo.isGecko){// firefox overflow auto bug workaround
43927             this.bodyEl.clip();
43928             if(this.tabs) {
43929                 this.tabs.bodyEl.clip();
43930             }
43931             if(this.activePanel){
43932                 this.activePanel.getEl().clip();
43933                 
43934                 if(this.activePanel.beforeSlide){
43935                     this.activePanel.beforeSlide();
43936                 }
43937             }
43938         }
43939     },
43940     
43941     afterSlide : function(){
43942         if(Roo.isGecko){// firefox overflow auto bug workaround
43943             this.bodyEl.unclip();
43944             if(this.tabs) {
43945                 this.tabs.bodyEl.unclip();
43946             }
43947             if(this.activePanel){
43948                 this.activePanel.getEl().unclip();
43949                 if(this.activePanel.afterSlide){
43950                     this.activePanel.afterSlide();
43951                 }
43952             }
43953         }
43954     },
43955
43956     initAutoHide : function(){
43957         if(this.autoHide !== false){
43958             if(!this.autoHideHd){
43959                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43960                 this.autoHideHd = {
43961                     "mouseout": function(e){
43962                         if(!e.within(this.el, true)){
43963                             st.delay(500);
43964                         }
43965                     },
43966                     "mouseover" : function(e){
43967                         st.cancel();
43968                     },
43969                     scope : this
43970                 };
43971             }
43972             this.el.on(this.autoHideHd);
43973         }
43974     },
43975
43976     clearAutoHide : function(){
43977         if(this.autoHide !== false){
43978             this.el.un("mouseout", this.autoHideHd.mouseout);
43979             this.el.un("mouseover", this.autoHideHd.mouseover);
43980         }
43981     },
43982
43983     clearMonitor : function(){
43984         Roo.get(document).un("click", this.slideInIf, this);
43985     },
43986
43987     // these names are backwards but not changed for compat
43988     slideOut : function(){
43989         if(this.isSlid || this.el.hasActiveFx()){
43990             return;
43991         }
43992         this.isSlid = true;
43993         if(this.collapseBtn){
43994             this.collapseBtn.hide();
43995         }
43996         this.closeBtnState = this.closeBtn.getStyle('display');
43997         this.closeBtn.hide();
43998         if(this.stickBtn){
43999             this.stickBtn.show();
44000         }
44001         this.el.show();
44002         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44003         this.beforeSlide();
44004         this.el.setStyle("z-index", 10001);
44005         this.el.slideIn(this.getSlideAnchor(), {
44006             callback: function(){
44007                 this.afterSlide();
44008                 this.initAutoHide();
44009                 Roo.get(document).on("click", this.slideInIf, this);
44010                 this.fireEvent("slideshow", this);
44011             },
44012             scope: this,
44013             block: true
44014         });
44015     },
44016
44017     afterSlideIn : function(){
44018         this.clearAutoHide();
44019         this.isSlid = false;
44020         this.clearMonitor();
44021         this.el.setStyle("z-index", "");
44022         if(this.collapseBtn){
44023             this.collapseBtn.show();
44024         }
44025         this.closeBtn.setStyle('display', this.closeBtnState);
44026         if(this.stickBtn){
44027             this.stickBtn.hide();
44028         }
44029         this.fireEvent("slidehide", this);
44030     },
44031
44032     slideIn : function(cb){
44033         if(!this.isSlid || this.el.hasActiveFx()){
44034             Roo.callback(cb);
44035             return;
44036         }
44037         this.isSlid = false;
44038         this.beforeSlide();
44039         this.el.slideOut(this.getSlideAnchor(), {
44040             callback: function(){
44041                 this.el.setLeftTop(-10000, -10000);
44042                 this.afterSlide();
44043                 this.afterSlideIn();
44044                 Roo.callback(cb);
44045             },
44046             scope: this,
44047             block: true
44048         });
44049     },
44050     
44051     slideInIf : function(e){
44052         if(!e.within(this.el)){
44053             this.slideIn();
44054         }
44055     },
44056
44057     animateCollapse : function(){
44058         this.beforeSlide();
44059         this.el.setStyle("z-index", 20000);
44060         var anchor = this.getSlideAnchor();
44061         this.el.slideOut(anchor, {
44062             callback : function(){
44063                 this.el.setStyle("z-index", "");
44064                 this.collapsedEl.slideIn(anchor, {duration:.3});
44065                 this.afterSlide();
44066                 this.el.setLocation(-10000,-10000);
44067                 this.el.hide();
44068                 this.fireEvent("collapsed", this);
44069             },
44070             scope: this,
44071             block: true
44072         });
44073     },
44074
44075     animateExpand : function(){
44076         this.beforeSlide();
44077         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44078         this.el.setStyle("z-index", 20000);
44079         this.collapsedEl.hide({
44080             duration:.1
44081         });
44082         this.el.slideIn(this.getSlideAnchor(), {
44083             callback : function(){
44084                 this.el.setStyle("z-index", "");
44085                 this.afterSlide();
44086                 if(this.split){
44087                     this.split.el.show();
44088                 }
44089                 this.fireEvent("invalidated", this);
44090                 this.fireEvent("expanded", this);
44091             },
44092             scope: this,
44093             block: true
44094         });
44095     },
44096
44097     anchors : {
44098         "west" : "left",
44099         "east" : "right",
44100         "north" : "top",
44101         "south" : "bottom"
44102     },
44103
44104     sanchors : {
44105         "west" : "l",
44106         "east" : "r",
44107         "north" : "t",
44108         "south" : "b"
44109     },
44110
44111     canchors : {
44112         "west" : "tl-tr",
44113         "east" : "tr-tl",
44114         "north" : "tl-bl",
44115         "south" : "bl-tl"
44116     },
44117
44118     getAnchor : function(){
44119         return this.anchors[this.position];
44120     },
44121
44122     getCollapseAnchor : function(){
44123         return this.canchors[this.position];
44124     },
44125
44126     getSlideAnchor : function(){
44127         return this.sanchors[this.position];
44128     },
44129
44130     getAlignAdj : function(){
44131         var cm = this.cmargins;
44132         switch(this.position){
44133             case "west":
44134                 return [0, 0];
44135             break;
44136             case "east":
44137                 return [0, 0];
44138             break;
44139             case "north":
44140                 return [0, 0];
44141             break;
44142             case "south":
44143                 return [0, 0];
44144             break;
44145         }
44146     },
44147
44148     getExpandAdj : function(){
44149         var c = this.collapsedEl, cm = this.cmargins;
44150         switch(this.position){
44151             case "west":
44152                 return [-(cm.right+c.getWidth()+cm.left), 0];
44153             break;
44154             case "east":
44155                 return [cm.right+c.getWidth()+cm.left, 0];
44156             break;
44157             case "north":
44158                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44159             break;
44160             case "south":
44161                 return [0, cm.top+cm.bottom+c.getHeight()];
44162             break;
44163         }
44164     }
44165 });/*
44166  * Based on:
44167  * Ext JS Library 1.1.1
44168  * Copyright(c) 2006-2007, Ext JS, LLC.
44169  *
44170  * Originally Released Under LGPL - original licence link has changed is not relivant.
44171  *
44172  * Fork - LGPL
44173  * <script type="text/javascript">
44174  */
44175 /*
44176  * These classes are private internal classes
44177  */
44178 Roo.bootstrap.layout.Center = function(config){
44179     config.region = "center";
44180     Roo.bootstrap.layout.Region.call(this, config);
44181     this.visible = true;
44182     this.minWidth = config.minWidth || 20;
44183     this.minHeight = config.minHeight || 20;
44184 };
44185
44186 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44187     hide : function(){
44188         // center panel can't be hidden
44189     },
44190     
44191     show : function(){
44192         // center panel can't be hidden
44193     },
44194     
44195     getMinWidth: function(){
44196         return this.minWidth;
44197     },
44198     
44199     getMinHeight: function(){
44200         return this.minHeight;
44201     }
44202 });
44203
44204
44205
44206
44207  
44208
44209
44210
44211
44212
44213
44214 Roo.bootstrap.layout.North = function(config)
44215 {
44216     config.region = 'north';
44217     config.cursor = 'n-resize';
44218     
44219     Roo.bootstrap.layout.Split.call(this, config);
44220     
44221     
44222     if(this.split){
44223         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44224         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44225         this.split.el.addClass("roo-layout-split-v");
44226     }
44227     //var size = config.initialSize || config.height;
44228     //if(this.el && typeof size != "undefined"){
44229     //    this.el.setHeight(size);
44230     //}
44231 };
44232 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44233 {
44234     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44235      
44236      
44237     onRender : function(ctr, pos)
44238     {
44239         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44240         var size = this.config.initialSize || this.config.height;
44241         if(this.el && typeof size != "undefined"){
44242             this.el.setHeight(size);
44243         }
44244     
44245     },
44246     
44247     getBox : function(){
44248         if(this.collapsed){
44249             return this.collapsedEl.getBox();
44250         }
44251         var box = this.el.getBox();
44252         if(this.split){
44253             box.height += this.split.el.getHeight();
44254         }
44255         return box;
44256     },
44257     
44258     updateBox : function(box){
44259         if(this.split && !this.collapsed){
44260             box.height -= this.split.el.getHeight();
44261             this.split.el.setLeft(box.x);
44262             this.split.el.setTop(box.y+box.height);
44263             this.split.el.setWidth(box.width);
44264         }
44265         if(this.collapsed){
44266             this.updateBody(box.width, null);
44267         }
44268         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44269     }
44270 });
44271
44272
44273
44274
44275
44276 Roo.bootstrap.layout.South = function(config){
44277     config.region = 'south';
44278     config.cursor = 's-resize';
44279     Roo.bootstrap.layout.Split.call(this, config);
44280     if(this.split){
44281         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44282         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44283         this.split.el.addClass("roo-layout-split-v");
44284     }
44285     
44286 };
44287
44288 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44289     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44290     
44291     onRender : function(ctr, pos)
44292     {
44293         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44294         var size = this.config.initialSize || this.config.height;
44295         if(this.el && typeof size != "undefined"){
44296             this.el.setHeight(size);
44297         }
44298     
44299     },
44300     
44301     getBox : function(){
44302         if(this.collapsed){
44303             return this.collapsedEl.getBox();
44304         }
44305         var box = this.el.getBox();
44306         if(this.split){
44307             var sh = this.split.el.getHeight();
44308             box.height += sh;
44309             box.y -= sh;
44310         }
44311         return box;
44312     },
44313     
44314     updateBox : function(box){
44315         if(this.split && !this.collapsed){
44316             var sh = this.split.el.getHeight();
44317             box.height -= sh;
44318             box.y += sh;
44319             this.split.el.setLeft(box.x);
44320             this.split.el.setTop(box.y-sh);
44321             this.split.el.setWidth(box.width);
44322         }
44323         if(this.collapsed){
44324             this.updateBody(box.width, null);
44325         }
44326         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44327     }
44328 });
44329
44330 Roo.bootstrap.layout.East = function(config){
44331     config.region = "east";
44332     config.cursor = "e-resize";
44333     Roo.bootstrap.layout.Split.call(this, config);
44334     if(this.split){
44335         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44336         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44337         this.split.el.addClass("roo-layout-split-h");
44338     }
44339     
44340 };
44341 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44342     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44343     
44344     onRender : function(ctr, pos)
44345     {
44346         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44347         var size = this.config.initialSize || this.config.width;
44348         if(this.el && typeof size != "undefined"){
44349             this.el.setWidth(size);
44350         }
44351     
44352     },
44353     
44354     getBox : function(){
44355         if(this.collapsed){
44356             return this.collapsedEl.getBox();
44357         }
44358         var box = this.el.getBox();
44359         if(this.split){
44360             var sw = this.split.el.getWidth();
44361             box.width += sw;
44362             box.x -= sw;
44363         }
44364         return box;
44365     },
44366
44367     updateBox : function(box){
44368         if(this.split && !this.collapsed){
44369             var sw = this.split.el.getWidth();
44370             box.width -= sw;
44371             this.split.el.setLeft(box.x);
44372             this.split.el.setTop(box.y);
44373             this.split.el.setHeight(box.height);
44374             box.x += sw;
44375         }
44376         if(this.collapsed){
44377             this.updateBody(null, box.height);
44378         }
44379         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44380     }
44381 });
44382
44383 Roo.bootstrap.layout.West = function(config){
44384     config.region = "west";
44385     config.cursor = "w-resize";
44386     
44387     Roo.bootstrap.layout.Split.call(this, config);
44388     if(this.split){
44389         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44390         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44391         this.split.el.addClass("roo-layout-split-h");
44392     }
44393     
44394 };
44395 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44396     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44397     
44398     onRender: function(ctr, pos)
44399     {
44400         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44401         var size = this.config.initialSize || this.config.width;
44402         if(typeof size != "undefined"){
44403             this.el.setWidth(size);
44404         }
44405     },
44406     
44407     getBox : function(){
44408         if(this.collapsed){
44409             return this.collapsedEl.getBox();
44410         }
44411         var box = this.el.getBox();
44412         if (box.width == 0) {
44413             box.width = this.config.width; // kludge?
44414         }
44415         if(this.split){
44416             box.width += this.split.el.getWidth();
44417         }
44418         return box;
44419     },
44420     
44421     updateBox : function(box){
44422         if(this.split && !this.collapsed){
44423             var sw = this.split.el.getWidth();
44424             box.width -= sw;
44425             this.split.el.setLeft(box.x+box.width);
44426             this.split.el.setTop(box.y);
44427             this.split.el.setHeight(box.height);
44428         }
44429         if(this.collapsed){
44430             this.updateBody(null, box.height);
44431         }
44432         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44433     }
44434 });/*
44435  * Based on:
44436  * Ext JS Library 1.1.1
44437  * Copyright(c) 2006-2007, Ext JS, LLC.
44438  *
44439  * Originally Released Under LGPL - original licence link has changed is not relivant.
44440  *
44441  * Fork - LGPL
44442  * <script type="text/javascript">
44443  */
44444 /**
44445  * @class Roo.bootstrap.paenl.Content
44446  * @extends Roo.util.Observable
44447  * @children Roo.bootstrap.Component
44448  * @parent builder Roo.bootstrap.layout.Border
44449  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44450  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44451  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44452  * @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
44453  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44454  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44455  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44456  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44457  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44458  * @cfg {String} title          The title for this panel
44459  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44460  * @cfg {String} url            Calls {@link #setUrl} with this value
44461  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44462  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44463  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44464  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44465  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44466  * @cfg {Boolean} badges render the badges
44467  * @cfg {String} cls  extra classes to use  
44468  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44469  
44470  * @constructor
44471  * Create a new ContentPanel.
44472  * @param {String/Object} config A string to set only the title or a config object
44473  
44474  */
44475 Roo.bootstrap.panel.Content = function( config){
44476     
44477     this.tpl = config.tpl || false;
44478     
44479     var el = config.el;
44480     var content = config.content;
44481
44482     if(config.autoCreate){ // xtype is available if this is called from factory
44483         el = Roo.id();
44484     }
44485     this.el = Roo.get(el);
44486     if(!this.el && config && config.autoCreate){
44487         if(typeof config.autoCreate == "object"){
44488             if(!config.autoCreate.id){
44489                 config.autoCreate.id = config.id||el;
44490             }
44491             this.el = Roo.DomHelper.append(document.body,
44492                         config.autoCreate, true);
44493         }else{
44494             var elcfg =  {
44495                 tag: "div",
44496                 cls: (config.cls || '') +
44497                     (config.background ? ' bg-' + config.background : '') +
44498                     " roo-layout-inactive-content",
44499                 id: config.id||el
44500             };
44501             if (config.iframe) {
44502                 elcfg.cn = [
44503                     {
44504                         tag : 'iframe',
44505                         style : 'border: 0px',
44506                         src : 'about:blank'
44507                     }
44508                 ];
44509             }
44510               
44511             if (config.html) {
44512                 elcfg.html = config.html;
44513                 
44514             }
44515                         
44516             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44517             if (config.iframe) {
44518                 this.iframeEl = this.el.select('iframe',true).first();
44519             }
44520             
44521         }
44522     } 
44523     this.closable = false;
44524     this.loaded = false;
44525     this.active = false;
44526    
44527       
44528     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44529         
44530         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44531         
44532         this.wrapEl = this.el; //this.el.wrap();
44533         var ti = [];
44534         if (config.toolbar.items) {
44535             ti = config.toolbar.items ;
44536             delete config.toolbar.items ;
44537         }
44538         
44539         var nitems = [];
44540         this.toolbar.render(this.wrapEl, 'before');
44541         for(var i =0;i < ti.length;i++) {
44542           //  Roo.log(['add child', items[i]]);
44543             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44544         }
44545         this.toolbar.items = nitems;
44546         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44547         delete config.toolbar;
44548         
44549     }
44550     /*
44551     // xtype created footer. - not sure if will work as we normally have to render first..
44552     if (this.footer && !this.footer.el && this.footer.xtype) {
44553         if (!this.wrapEl) {
44554             this.wrapEl = this.el.wrap();
44555         }
44556     
44557         this.footer.container = this.wrapEl.createChild();
44558          
44559         this.footer = Roo.factory(this.footer, Roo);
44560         
44561     }
44562     */
44563     
44564      if(typeof config == "string"){
44565         this.title = config;
44566     }else{
44567         Roo.apply(this, config);
44568     }
44569     
44570     if(this.resizeEl){
44571         this.resizeEl = Roo.get(this.resizeEl, true);
44572     }else{
44573         this.resizeEl = this.el;
44574     }
44575     // handle view.xtype
44576     
44577  
44578     
44579     
44580     this.addEvents({
44581         /**
44582          * @event activate
44583          * Fires when this panel is activated. 
44584          * @param {Roo.ContentPanel} this
44585          */
44586         "activate" : true,
44587         /**
44588          * @event deactivate
44589          * Fires when this panel is activated. 
44590          * @param {Roo.ContentPanel} this
44591          */
44592         "deactivate" : true,
44593
44594         /**
44595          * @event resize
44596          * Fires when this panel is resized if fitToFrame is true.
44597          * @param {Roo.ContentPanel} this
44598          * @param {Number} width The width after any component adjustments
44599          * @param {Number} height The height after any component adjustments
44600          */
44601         "resize" : true,
44602         
44603          /**
44604          * @event render
44605          * Fires when this tab is created
44606          * @param {Roo.ContentPanel} this
44607          */
44608         "render" : true,
44609         
44610           /**
44611          * @event scroll
44612          * Fires when this content is scrolled
44613          * @param {Roo.ContentPanel} this
44614          * @param {Event} scrollEvent
44615          */
44616         "scroll" : true
44617         
44618         
44619         
44620     });
44621     
44622
44623     
44624     
44625     if(this.autoScroll && !this.iframe){
44626         this.resizeEl.setStyle("overflow", "auto");
44627         this.resizeEl.on('scroll', this.onScroll, this);
44628     } else {
44629         // fix randome scrolling
44630         //this.el.on('scroll', function() {
44631         //    Roo.log('fix random scolling');
44632         //    this.scrollTo('top',0); 
44633         //});
44634     }
44635     content = content || this.content;
44636     if(content){
44637         this.setContent(content);
44638     }
44639     if(config && config.url){
44640         this.setUrl(this.url, this.params, this.loadOnce);
44641     }
44642     
44643     
44644     
44645     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44646     
44647     if (this.view && typeof(this.view.xtype) != 'undefined') {
44648         this.view.el = this.el.appendChild(document.createElement("div"));
44649         this.view = Roo.factory(this.view); 
44650         this.view.render  &&  this.view.render(false, '');  
44651     }
44652     
44653     
44654     this.fireEvent('render', this);
44655 };
44656
44657 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44658     
44659     cls : '',
44660     background : '',
44661     
44662     tabTip : '',
44663     
44664     iframe : false,
44665     iframeEl : false,
44666     
44667     /* Resize Element - use this to work out scroll etc. */
44668     resizeEl : false,
44669     
44670     setRegion : function(region){
44671         this.region = region;
44672         this.setActiveClass(region && !this.background);
44673     },
44674     
44675     
44676     setActiveClass: function(state)
44677     {
44678         if(state){
44679            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44680            this.el.setStyle('position','relative');
44681         }else{
44682            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44683            this.el.setStyle('position', 'absolute');
44684         } 
44685     },
44686     
44687     /**
44688      * Returns the toolbar for this Panel if one was configured. 
44689      * @return {Roo.Toolbar} 
44690      */
44691     getToolbar : function(){
44692         return this.toolbar;
44693     },
44694     
44695     setActiveState : function(active)
44696     {
44697         this.active = active;
44698         this.setActiveClass(active);
44699         if(!active){
44700             if(this.fireEvent("deactivate", this) === false){
44701                 return false;
44702             }
44703             return true;
44704         }
44705         this.fireEvent("activate", this);
44706         return true;
44707     },
44708     /**
44709      * Updates this panel's element (not for iframe)
44710      * @param {String} content The new content
44711      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44712     */
44713     setContent : function(content, loadScripts){
44714         if (this.iframe) {
44715             return;
44716         }
44717         
44718         this.el.update(content, loadScripts);
44719     },
44720
44721     ignoreResize : function(w, h)
44722     {
44723         //return false; // always resize?
44724         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44725             return true;
44726         }else{
44727             this.lastSize = {width: w, height: h};
44728             return false;
44729         }
44730     },
44731     /**
44732      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44733      * @return {Roo.UpdateManager} The UpdateManager
44734      */
44735     getUpdateManager : function(){
44736         if (this.iframe) {
44737             return false;
44738         }
44739         return this.el.getUpdateManager();
44740     },
44741      /**
44742      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44743      * Does not work with IFRAME contents
44744      * @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:
44745 <pre><code>
44746 panel.load({
44747     url: "your-url.php",
44748     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44749     callback: yourFunction,
44750     scope: yourObject, //(optional scope)
44751     discardUrl: false,
44752     nocache: false,
44753     text: "Loading...",
44754     timeout: 30,
44755     scripts: false
44756 });
44757 </code></pre>
44758      
44759      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44760      * 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.
44761      * @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}
44762      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44763      * @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.
44764      * @return {Roo.ContentPanel} this
44765      */
44766     load : function(){
44767         
44768         if (this.iframe) {
44769             return this;
44770         }
44771         
44772         var um = this.el.getUpdateManager();
44773         um.update.apply(um, arguments);
44774         return this;
44775     },
44776
44777
44778     /**
44779      * 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.
44780      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44781      * @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)
44782      * @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)
44783      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44784      */
44785     setUrl : function(url, params, loadOnce){
44786         if (this.iframe) {
44787             this.iframeEl.dom.src = url;
44788             return false;
44789         }
44790         
44791         if(this.refreshDelegate){
44792             this.removeListener("activate", this.refreshDelegate);
44793         }
44794         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44795         this.on("activate", this.refreshDelegate);
44796         return this.el.getUpdateManager();
44797     },
44798     
44799     _handleRefresh : function(url, params, loadOnce){
44800         if(!loadOnce || !this.loaded){
44801             var updater = this.el.getUpdateManager();
44802             updater.update(url, params, this._setLoaded.createDelegate(this));
44803         }
44804     },
44805     
44806     _setLoaded : function(){
44807         this.loaded = true;
44808     }, 
44809     
44810     /**
44811      * Returns this panel's id
44812      * @return {String} 
44813      */
44814     getId : function(){
44815         return this.el.id;
44816     },
44817     
44818     /** 
44819      * Returns this panel's element - used by regiosn to add.
44820      * @return {Roo.Element} 
44821      */
44822     getEl : function(){
44823         return this.wrapEl || this.el;
44824     },
44825     
44826    
44827     
44828     adjustForComponents : function(width, height)
44829     {
44830         //Roo.log('adjustForComponents ');
44831         if(this.resizeEl != this.el){
44832             width -= this.el.getFrameWidth('lr');
44833             height -= this.el.getFrameWidth('tb');
44834         }
44835         if(this.toolbar){
44836             var te = this.toolbar.getEl();
44837             te.setWidth(width);
44838             height -= te.getHeight();
44839         }
44840         if(this.footer){
44841             var te = this.footer.getEl();
44842             te.setWidth(width);
44843             height -= te.getHeight();
44844         }
44845         
44846         
44847         if(this.adjustments){
44848             width += this.adjustments[0];
44849             height += this.adjustments[1];
44850         }
44851         return {"width": width, "height": height};
44852     },
44853     
44854     setSize : function(width, height){
44855         if(this.fitToFrame && !this.ignoreResize(width, height)){
44856             if(this.fitContainer && this.resizeEl != this.el){
44857                 this.el.setSize(width, height);
44858             }
44859             var size = this.adjustForComponents(width, height);
44860             if (this.iframe) {
44861                 this.iframeEl.setSize(width,height);
44862             }
44863             
44864             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44865             this.fireEvent('resize', this, size.width, size.height);
44866             
44867             
44868         }
44869     },
44870     
44871     /**
44872      * Returns this panel's title
44873      * @return {String} 
44874      */
44875     getTitle : function(){
44876         
44877         if (typeof(this.title) != 'object') {
44878             return this.title;
44879         }
44880         
44881         var t = '';
44882         for (var k in this.title) {
44883             if (!this.title.hasOwnProperty(k)) {
44884                 continue;
44885             }
44886             
44887             if (k.indexOf('-') >= 0) {
44888                 var s = k.split('-');
44889                 for (var i = 0; i<s.length; i++) {
44890                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44891                 }
44892             } else {
44893                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44894             }
44895         }
44896         return t;
44897     },
44898     
44899     /**
44900      * Set this panel's title
44901      * @param {String} title
44902      */
44903     setTitle : function(title){
44904         this.title = title;
44905         if(this.region){
44906             this.region.updatePanelTitle(this, title);
44907         }
44908     },
44909     
44910     /**
44911      * Returns true is this panel was configured to be closable
44912      * @return {Boolean} 
44913      */
44914     isClosable : function(){
44915         return this.closable;
44916     },
44917     
44918     beforeSlide : function(){
44919         this.el.clip();
44920         this.resizeEl.clip();
44921     },
44922     
44923     afterSlide : function(){
44924         this.el.unclip();
44925         this.resizeEl.unclip();
44926     },
44927     
44928     /**
44929      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44930      *   Will fail silently if the {@link #setUrl} method has not been called.
44931      *   This does not activate the panel, just updates its content.
44932      */
44933     refresh : function(){
44934         if(this.refreshDelegate){
44935            this.loaded = false;
44936            this.refreshDelegate();
44937         }
44938     },
44939     
44940     /**
44941      * Destroys this panel
44942      */
44943     destroy : function(){
44944         this.el.removeAllListeners();
44945         var tempEl = document.createElement("span");
44946         tempEl.appendChild(this.el.dom);
44947         tempEl.innerHTML = "";
44948         this.el.remove();
44949         this.el = null;
44950     },
44951     
44952     /**
44953      * form - if the content panel contains a form - this is a reference to it.
44954      * @type {Roo.form.Form}
44955      */
44956     form : false,
44957     /**
44958      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44959      *    This contains a reference to it.
44960      * @type {Roo.View}
44961      */
44962     view : false,
44963     
44964       /**
44965      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44966      * <pre><code>
44967
44968 layout.addxtype({
44969        xtype : 'Form',
44970        items: [ .... ]
44971    }
44972 );
44973
44974 </code></pre>
44975      * @param {Object} cfg Xtype definition of item to add.
44976      */
44977     
44978     
44979     getChildContainer: function () {
44980         return this.getEl();
44981     },
44982     
44983     
44984     onScroll : function(e)
44985     {
44986         this.fireEvent('scroll', this, e);
44987     }
44988     
44989     
44990     /*
44991         var  ret = new Roo.factory(cfg);
44992         return ret;
44993         
44994         
44995         // add form..
44996         if (cfg.xtype.match(/^Form$/)) {
44997             
44998             var el;
44999             //if (this.footer) {
45000             //    el = this.footer.container.insertSibling(false, 'before');
45001             //} else {
45002                 el = this.el.createChild();
45003             //}
45004
45005             this.form = new  Roo.form.Form(cfg);
45006             
45007             
45008             if ( this.form.allItems.length) {
45009                 this.form.render(el.dom);
45010             }
45011             return this.form;
45012         }
45013         // should only have one of theses..
45014         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45015             // views.. should not be just added - used named prop 'view''
45016             
45017             cfg.el = this.el.appendChild(document.createElement("div"));
45018             // factory?
45019             
45020             var ret = new Roo.factory(cfg);
45021              
45022              ret.render && ret.render(false, ''); // render blank..
45023             this.view = ret;
45024             return ret;
45025         }
45026         return false;
45027     }
45028     \*/
45029 });
45030  
45031 /**
45032  * @class Roo.bootstrap.panel.Grid
45033  * @extends Roo.bootstrap.panel.Content
45034  * @constructor
45035  * Create a new GridPanel.
45036  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45037  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45038  * @param {Object} config A the config object
45039   
45040  */
45041
45042
45043
45044 Roo.bootstrap.panel.Grid = function(config)
45045 {
45046     
45047       
45048     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45049         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45050
45051     config.el = this.wrapper;
45052     //this.el = this.wrapper;
45053     
45054       if (config.container) {
45055         // ctor'ed from a Border/panel.grid
45056         
45057         
45058         this.wrapper.setStyle("overflow", "hidden");
45059         this.wrapper.addClass('roo-grid-container');
45060
45061     }
45062     
45063     
45064     if(config.toolbar){
45065         var tool_el = this.wrapper.createChild();    
45066         this.toolbar = Roo.factory(config.toolbar);
45067         var ti = [];
45068         if (config.toolbar.items) {
45069             ti = config.toolbar.items ;
45070             delete config.toolbar.items ;
45071         }
45072         
45073         var nitems = [];
45074         this.toolbar.render(tool_el);
45075         for(var i =0;i < ti.length;i++) {
45076           //  Roo.log(['add child', items[i]]);
45077             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45078         }
45079         this.toolbar.items = nitems;
45080         
45081         delete config.toolbar;
45082     }
45083     
45084     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45085     config.grid.scrollBody = true;;
45086     config.grid.monitorWindowResize = false; // turn off autosizing
45087     config.grid.autoHeight = false;
45088     config.grid.autoWidth = false;
45089     
45090     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45091     
45092     if (config.background) {
45093         // render grid on panel activation (if panel background)
45094         this.on('activate', function(gp) {
45095             if (!gp.grid.rendered) {
45096                 gp.grid.render(this.wrapper);
45097                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45098             }
45099         });
45100             
45101     } else {
45102         this.grid.render(this.wrapper);
45103         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45104
45105     }
45106     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45107     // ??? needed ??? config.el = this.wrapper;
45108     
45109     
45110     
45111   
45112     // xtype created footer. - not sure if will work as we normally have to render first..
45113     if (this.footer && !this.footer.el && this.footer.xtype) {
45114         
45115         var ctr = this.grid.getView().getFooterPanel(true);
45116         this.footer.dataSource = this.grid.dataSource;
45117         this.footer = Roo.factory(this.footer, Roo);
45118         this.footer.render(ctr);
45119         
45120     }
45121     
45122     
45123     
45124     
45125      
45126 };
45127
45128 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45129 {
45130   
45131     getId : function(){
45132         return this.grid.id;
45133     },
45134     
45135     /**
45136      * Returns the grid for this panel
45137      * @return {Roo.bootstrap.Table} 
45138      */
45139     getGrid : function(){
45140         return this.grid;    
45141     },
45142     
45143     setSize : function(width, height)
45144     {
45145      
45146         //if(!this.ignoreResize(width, height)){
45147             var grid = this.grid;
45148             var size = this.adjustForComponents(width, height);
45149             // tfoot is not a footer?
45150           
45151             
45152             var gridel = grid.getGridEl();
45153             gridel.setSize(size.width, size.height);
45154             
45155             var tbd = grid.getGridEl().select('tbody', true).first();
45156             var thd = grid.getGridEl().select('thead',true).first();
45157             var tbf= grid.getGridEl().select('tfoot', true).first();
45158
45159             if (tbf) {
45160                 size.height -= tbf.getHeight();
45161             }
45162             if (thd) {
45163                 size.height -= thd.getHeight();
45164             }
45165             
45166             tbd.setSize(size.width, size.height );
45167             // this is for the account management tab -seems to work there.
45168             var thd = grid.getGridEl().select('thead',true).first();
45169             //if (tbd) {
45170             //    tbd.setSize(size.width, size.height - thd.getHeight());
45171             //}
45172              
45173             grid.autoSize();
45174         //}
45175    
45176     },
45177      
45178     
45179     
45180     beforeSlide : function(){
45181         this.grid.getView().scroller.clip();
45182     },
45183     
45184     afterSlide : function(){
45185         this.grid.getView().scroller.unclip();
45186     },
45187     
45188     destroy : function(){
45189         this.grid.destroy();
45190         delete this.grid;
45191         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45192     }
45193 });
45194
45195 /**
45196  * @class Roo.bootstrap.panel.Nest
45197  * @extends Roo.bootstrap.panel.Content
45198  * @constructor
45199  * Create a new Panel, that can contain a layout.Border.
45200  * 
45201  * 
45202  * @param {String/Object} config A string to set only the title or a config object
45203  */
45204 Roo.bootstrap.panel.Nest = function(config)
45205 {
45206     // construct with only one argument..
45207     /* FIXME - implement nicer consturctors
45208     if (layout.layout) {
45209         config = layout;
45210         layout = config.layout;
45211         delete config.layout;
45212     }
45213     if (layout.xtype && !layout.getEl) {
45214         // then layout needs constructing..
45215         layout = Roo.factory(layout, Roo);
45216     }
45217     */
45218     
45219     config.el =  config.layout.getEl();
45220     
45221     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45222     
45223     config.layout.monitorWindowResize = false; // turn off autosizing
45224     this.layout = config.layout;
45225     this.layout.getEl().addClass("roo-layout-nested-layout");
45226     this.layout.parent = this;
45227     
45228     
45229     
45230     
45231 };
45232
45233 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45234     /**
45235     * @cfg {Roo.BorderLayout} layout The layout for this panel
45236     */
45237     layout : false,
45238
45239     setSize : function(width, height){
45240         if(!this.ignoreResize(width, height)){
45241             var size = this.adjustForComponents(width, height);
45242             var el = this.layout.getEl();
45243             if (size.height < 1) {
45244                 el.setWidth(size.width);   
45245             } else {
45246                 el.setSize(size.width, size.height);
45247             }
45248             var touch = el.dom.offsetWidth;
45249             this.layout.layout();
45250             // ie requires a double layout on the first pass
45251             if(Roo.isIE && !this.initialized){
45252                 this.initialized = true;
45253                 this.layout.layout();
45254             }
45255         }
45256     },
45257     
45258     // activate all subpanels if not currently active..
45259     
45260     setActiveState : function(active){
45261         this.active = active;
45262         this.setActiveClass(active);
45263         
45264         if(!active){
45265             this.fireEvent("deactivate", this);
45266             return;
45267         }
45268         
45269         this.fireEvent("activate", this);
45270         // not sure if this should happen before or after..
45271         if (!this.layout) {
45272             return; // should not happen..
45273         }
45274         var reg = false;
45275         for (var r in this.layout.regions) {
45276             reg = this.layout.getRegion(r);
45277             if (reg.getActivePanel()) {
45278                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45279                 reg.setActivePanel(reg.getActivePanel());
45280                 continue;
45281             }
45282             if (!reg.panels.length) {
45283                 continue;
45284             }
45285             reg.showPanel(reg.getPanel(0));
45286         }
45287         
45288         
45289         
45290         
45291     },
45292     
45293     /**
45294      * Returns the nested BorderLayout for this panel
45295      * @return {Roo.BorderLayout} 
45296      */
45297     getLayout : function(){
45298         return this.layout;
45299     },
45300     
45301      /**
45302      * Adds a xtype elements to the layout of the nested panel
45303      * <pre><code>
45304
45305 panel.addxtype({
45306        xtype : 'ContentPanel',
45307        region: 'west',
45308        items: [ .... ]
45309    }
45310 );
45311
45312 panel.addxtype({
45313         xtype : 'NestedLayoutPanel',
45314         region: 'west',
45315         layout: {
45316            center: { },
45317            west: { }   
45318         },
45319         items : [ ... list of content panels or nested layout panels.. ]
45320    }
45321 );
45322 </code></pre>
45323      * @param {Object} cfg Xtype definition of item to add.
45324      */
45325     addxtype : function(cfg) {
45326         return this.layout.addxtype(cfg);
45327     
45328     }
45329 });/*
45330  * Based on:
45331  * Ext JS Library 1.1.1
45332  * Copyright(c) 2006-2007, Ext JS, LLC.
45333  *
45334  * Originally Released Under LGPL - original licence link has changed is not relivant.
45335  *
45336  * Fork - LGPL
45337  * <script type="text/javascript">
45338  */
45339 /**
45340  * @class Roo.TabPanel
45341  * @extends Roo.util.Observable
45342  * A lightweight tab container.
45343  * <br><br>
45344  * Usage:
45345  * <pre><code>
45346 // basic tabs 1, built from existing content
45347 var tabs = new Roo.TabPanel("tabs1");
45348 tabs.addTab("script", "View Script");
45349 tabs.addTab("markup", "View Markup");
45350 tabs.activate("script");
45351
45352 // more advanced tabs, built from javascript
45353 var jtabs = new Roo.TabPanel("jtabs");
45354 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45355
45356 // set up the UpdateManager
45357 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45358 var updater = tab2.getUpdateManager();
45359 updater.setDefaultUrl("ajax1.htm");
45360 tab2.on('activate', updater.refresh, updater, true);
45361
45362 // Use setUrl for Ajax loading
45363 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45364 tab3.setUrl("ajax2.htm", null, true);
45365
45366 // Disabled tab
45367 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45368 tab4.disable();
45369
45370 jtabs.activate("jtabs-1");
45371  * </code></pre>
45372  * @constructor
45373  * Create a new TabPanel.
45374  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45375  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45376  */
45377 Roo.bootstrap.panel.Tabs = function(config){
45378     /**
45379     * The container element for this TabPanel.
45380     * @type Roo.Element
45381     */
45382     this.el = Roo.get(config.el);
45383     delete config.el;
45384     if(config){
45385         if(typeof config == "boolean"){
45386             this.tabPosition = config ? "bottom" : "top";
45387         }else{
45388             Roo.apply(this, config);
45389         }
45390     }
45391     
45392     if(this.tabPosition == "bottom"){
45393         // if tabs are at the bottom = create the body first.
45394         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45395         this.el.addClass("roo-tabs-bottom");
45396     }
45397     // next create the tabs holders
45398     
45399     if (this.tabPosition == "west"){
45400         
45401         var reg = this.region; // fake it..
45402         while (reg) {
45403             if (!reg.mgr.parent) {
45404                 break;
45405             }
45406             reg = reg.mgr.parent.region;
45407         }
45408         Roo.log("got nest?");
45409         Roo.log(reg);
45410         if (reg.mgr.getRegion('west')) {
45411             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45412             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45413             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45414             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45415             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45416         
45417             
45418         }
45419         
45420         
45421     } else {
45422      
45423         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45424         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45425         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45426         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45427     }
45428     
45429     
45430     if(Roo.isIE){
45431         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45432     }
45433     
45434     // finally - if tabs are at the top, then create the body last..
45435     if(this.tabPosition != "bottom"){
45436         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45437          * @type Roo.Element
45438          */
45439         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45440         this.el.addClass("roo-tabs-top");
45441     }
45442     this.items = [];
45443
45444     this.bodyEl.setStyle("position", "relative");
45445
45446     this.active = null;
45447     this.activateDelegate = this.activate.createDelegate(this);
45448
45449     this.addEvents({
45450         /**
45451          * @event tabchange
45452          * Fires when the active tab changes
45453          * @param {Roo.TabPanel} this
45454          * @param {Roo.TabPanelItem} activePanel The new active tab
45455          */
45456         "tabchange": true,
45457         /**
45458          * @event beforetabchange
45459          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45460          * @param {Roo.TabPanel} this
45461          * @param {Object} e Set cancel to true on this object to cancel the tab change
45462          * @param {Roo.TabPanelItem} tab The tab being changed to
45463          */
45464         "beforetabchange" : true
45465     });
45466
45467     Roo.EventManager.onWindowResize(this.onResize, this);
45468     this.cpad = this.el.getPadding("lr");
45469     this.hiddenCount = 0;
45470
45471
45472     // toolbar on the tabbar support...
45473     if (this.toolbar) {
45474         alert("no toolbar support yet");
45475         this.toolbar  = false;
45476         /*
45477         var tcfg = this.toolbar;
45478         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45479         this.toolbar = new Roo.Toolbar(tcfg);
45480         if (Roo.isSafari) {
45481             var tbl = tcfg.container.child('table', true);
45482             tbl.setAttribute('width', '100%');
45483         }
45484         */
45485         
45486     }
45487    
45488
45489
45490     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45491 };
45492
45493 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45494     /*
45495      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45496      */
45497     tabPosition : "top",
45498     /*
45499      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45500      */
45501     currentTabWidth : 0,
45502     /*
45503      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45504      */
45505     minTabWidth : 40,
45506     /*
45507      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45508      */
45509     maxTabWidth : 250,
45510     /*
45511      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45512      */
45513     preferredTabWidth : 175,
45514     /*
45515      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45516      */
45517     resizeTabs : false,
45518     /*
45519      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45520      */
45521     monitorResize : true,
45522     /*
45523      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45524      */
45525     toolbar : false,  // set by caller..
45526     
45527     region : false, /// set by caller
45528     
45529     disableTooltips : true, // not used yet...
45530
45531     /**
45532      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45533      * @param {String} id The id of the div to use <b>or create</b>
45534      * @param {String} text The text for the tab
45535      * @param {String} content (optional) Content to put in the TabPanelItem body
45536      * @param {Boolean} closable (optional) True to create a close icon on the tab
45537      * @return {Roo.TabPanelItem} The created TabPanelItem
45538      */
45539     addTab : function(id, text, content, closable, tpl)
45540     {
45541         var item = new Roo.bootstrap.panel.TabItem({
45542             panel: this,
45543             id : id,
45544             text : text,
45545             closable : closable,
45546             tpl : tpl
45547         });
45548         this.addTabItem(item);
45549         if(content){
45550             item.setContent(content);
45551         }
45552         return item;
45553     },
45554
45555     /**
45556      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45557      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45558      * @return {Roo.TabPanelItem}
45559      */
45560     getTab : function(id){
45561         return this.items[id];
45562     },
45563
45564     /**
45565      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45566      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45567      */
45568     hideTab : function(id){
45569         var t = this.items[id];
45570         if(!t.isHidden()){
45571            t.setHidden(true);
45572            this.hiddenCount++;
45573            this.autoSizeTabs();
45574         }
45575     },
45576
45577     /**
45578      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45579      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45580      */
45581     unhideTab : function(id){
45582         var t = this.items[id];
45583         if(t.isHidden()){
45584            t.setHidden(false);
45585            this.hiddenCount--;
45586            this.autoSizeTabs();
45587         }
45588     },
45589
45590     /**
45591      * Adds an existing {@link Roo.TabPanelItem}.
45592      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45593      */
45594     addTabItem : function(item)
45595     {
45596         this.items[item.id] = item;
45597         this.items.push(item);
45598         this.autoSizeTabs();
45599       //  if(this.resizeTabs){
45600     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45601   //         this.autoSizeTabs();
45602 //        }else{
45603 //            item.autoSize();
45604        // }
45605     },
45606
45607     /**
45608      * Removes a {@link Roo.TabPanelItem}.
45609      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45610      */
45611     removeTab : function(id){
45612         var items = this.items;
45613         var tab = items[id];
45614         if(!tab) { return; }
45615         var index = items.indexOf(tab);
45616         if(this.active == tab && items.length > 1){
45617             var newTab = this.getNextAvailable(index);
45618             if(newTab) {
45619                 newTab.activate();
45620             }
45621         }
45622         this.stripEl.dom.removeChild(tab.pnode.dom);
45623         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45624             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45625         }
45626         items.splice(index, 1);
45627         delete this.items[tab.id];
45628         tab.fireEvent("close", tab);
45629         tab.purgeListeners();
45630         this.autoSizeTabs();
45631     },
45632
45633     getNextAvailable : function(start){
45634         var items = this.items;
45635         var index = start;
45636         // look for a next tab that will slide over to
45637         // replace the one being removed
45638         while(index < items.length){
45639             var item = items[++index];
45640             if(item && !item.isHidden()){
45641                 return item;
45642             }
45643         }
45644         // if one isn't found select the previous tab (on the left)
45645         index = start;
45646         while(index >= 0){
45647             var item = items[--index];
45648             if(item && !item.isHidden()){
45649                 return item;
45650             }
45651         }
45652         return null;
45653     },
45654
45655     /**
45656      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45657      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45658      */
45659     disableTab : function(id){
45660         var tab = this.items[id];
45661         if(tab && this.active != tab){
45662             tab.disable();
45663         }
45664     },
45665
45666     /**
45667      * Enables a {@link Roo.TabPanelItem} that is disabled.
45668      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45669      */
45670     enableTab : function(id){
45671         var tab = this.items[id];
45672         tab.enable();
45673     },
45674
45675     /**
45676      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45677      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45678      * @return {Roo.TabPanelItem} The TabPanelItem.
45679      */
45680     activate : function(id)
45681     {
45682         //Roo.log('activite:'  + id);
45683         
45684         var tab = this.items[id];
45685         if(!tab){
45686             return null;
45687         }
45688         if(tab == this.active || tab.disabled){
45689             return tab;
45690         }
45691         var e = {};
45692         this.fireEvent("beforetabchange", this, e, tab);
45693         if(e.cancel !== true && !tab.disabled){
45694             if(this.active){
45695                 this.active.hide();
45696             }
45697             this.active = this.items[id];
45698             this.active.show();
45699             this.fireEvent("tabchange", this, this.active);
45700         }
45701         return tab;
45702     },
45703
45704     /**
45705      * Gets the active {@link Roo.TabPanelItem}.
45706      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45707      */
45708     getActiveTab : function(){
45709         return this.active;
45710     },
45711
45712     /**
45713      * Updates the tab body element to fit the height of the container element
45714      * for overflow scrolling
45715      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45716      */
45717     syncHeight : function(targetHeight){
45718         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45719         var bm = this.bodyEl.getMargins();
45720         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45721         this.bodyEl.setHeight(newHeight);
45722         return newHeight;
45723     },
45724
45725     onResize : function(){
45726         if(this.monitorResize){
45727             this.autoSizeTabs();
45728         }
45729     },
45730
45731     /**
45732      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45733      */
45734     beginUpdate : function(){
45735         this.updating = true;
45736     },
45737
45738     /**
45739      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45740      */
45741     endUpdate : function(){
45742         this.updating = false;
45743         this.autoSizeTabs();
45744     },
45745
45746     /**
45747      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45748      */
45749     autoSizeTabs : function()
45750     {
45751         var count = this.items.length;
45752         var vcount = count - this.hiddenCount;
45753         
45754         if (vcount < 2) {
45755             this.stripEl.hide();
45756         } else {
45757             this.stripEl.show();
45758         }
45759         
45760         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45761             return;
45762         }
45763         
45764         
45765         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45766         var availWidth = Math.floor(w / vcount);
45767         var b = this.stripBody;
45768         if(b.getWidth() > w){
45769             var tabs = this.items;
45770             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45771             if(availWidth < this.minTabWidth){
45772                 /*if(!this.sleft){    // incomplete scrolling code
45773                     this.createScrollButtons();
45774                 }
45775                 this.showScroll();
45776                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45777             }
45778         }else{
45779             if(this.currentTabWidth < this.preferredTabWidth){
45780                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45781             }
45782         }
45783     },
45784
45785     /**
45786      * Returns the number of tabs in this TabPanel.
45787      * @return {Number}
45788      */
45789      getCount : function(){
45790          return this.items.length;
45791      },
45792
45793     /**
45794      * Resizes all the tabs to the passed width
45795      * @param {Number} The new width
45796      */
45797     setTabWidth : function(width){
45798         this.currentTabWidth = width;
45799         for(var i = 0, len = this.items.length; i < len; i++) {
45800                 if(!this.items[i].isHidden()) {
45801                 this.items[i].setWidth(width);
45802             }
45803         }
45804     },
45805
45806     /**
45807      * Destroys this TabPanel
45808      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45809      */
45810     destroy : function(removeEl){
45811         Roo.EventManager.removeResizeListener(this.onResize, this);
45812         for(var i = 0, len = this.items.length; i < len; i++){
45813             this.items[i].purgeListeners();
45814         }
45815         if(removeEl === true){
45816             this.el.update("");
45817             this.el.remove();
45818         }
45819     },
45820     
45821     createStrip : function(container)
45822     {
45823         var strip = document.createElement("nav");
45824         strip.className = Roo.bootstrap.version == 4 ?
45825             "navbar-light bg-light" : 
45826             "navbar navbar-default"; //"x-tabs-wrap";
45827         container.appendChild(strip);
45828         return strip;
45829     },
45830     
45831     createStripList : function(strip)
45832     {
45833         // div wrapper for retard IE
45834         // returns the "tr" element.
45835         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45836         //'<div class="x-tabs-strip-wrap">'+
45837           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45838           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45839         return strip.firstChild; //.firstChild.firstChild.firstChild;
45840     },
45841     createBody : function(container)
45842     {
45843         var body = document.createElement("div");
45844         Roo.id(body, "tab-body");
45845         //Roo.fly(body).addClass("x-tabs-body");
45846         Roo.fly(body).addClass("tab-content");
45847         container.appendChild(body);
45848         return body;
45849     },
45850     createItemBody :function(bodyEl, id){
45851         var body = Roo.getDom(id);
45852         if(!body){
45853             body = document.createElement("div");
45854             body.id = id;
45855         }
45856         //Roo.fly(body).addClass("x-tabs-item-body");
45857         Roo.fly(body).addClass("tab-pane");
45858          bodyEl.insertBefore(body, bodyEl.firstChild);
45859         return body;
45860     },
45861     /** @private */
45862     createStripElements :  function(stripEl, text, closable, tpl)
45863     {
45864         var td = document.createElement("li"); // was td..
45865         td.className = 'nav-item';
45866         
45867         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45868         
45869         
45870         stripEl.appendChild(td);
45871         /*if(closable){
45872             td.className = "x-tabs-closable";
45873             if(!this.closeTpl){
45874                 this.closeTpl = new Roo.Template(
45875                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45876                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45877                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45878                 );
45879             }
45880             var el = this.closeTpl.overwrite(td, {"text": text});
45881             var close = el.getElementsByTagName("div")[0];
45882             var inner = el.getElementsByTagName("em")[0];
45883             return {"el": el, "close": close, "inner": inner};
45884         } else {
45885         */
45886         // not sure what this is..
45887 //            if(!this.tabTpl){
45888                 //this.tabTpl = new Roo.Template(
45889                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45890                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45891                 //);
45892 //                this.tabTpl = new Roo.Template(
45893 //                   '<a href="#">' +
45894 //                   '<span unselectable="on"' +
45895 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45896 //                            ' >{text}</span></a>'
45897 //                );
45898 //                
45899 //            }
45900
45901
45902             var template = tpl || this.tabTpl || false;
45903             
45904             if(!template){
45905                 template =  new Roo.Template(
45906                         Roo.bootstrap.version == 4 ? 
45907                             (
45908                                 '<a class="nav-link" href="#" unselectable="on"' +
45909                                      (this.disableTooltips ? '' : ' title="{text}"') +
45910                                      ' >{text}</a>'
45911                             ) : (
45912                                 '<a class="nav-link" href="#">' +
45913                                 '<span unselectable="on"' +
45914                                          (this.disableTooltips ? '' : ' title="{text}"') +
45915                                     ' >{text}</span></a>'
45916                             )
45917                 );
45918             }
45919             
45920             switch (typeof(template)) {
45921                 case 'object' :
45922                     break;
45923                 case 'string' :
45924                     template = new Roo.Template(template);
45925                     break;
45926                 default :
45927                     break;
45928             }
45929             
45930             var el = template.overwrite(td, {"text": text});
45931             
45932             var inner = el.getElementsByTagName("span")[0];
45933             
45934             return {"el": el, "inner": inner};
45935             
45936     }
45937         
45938     
45939 });
45940
45941 /**
45942  * @class Roo.TabPanelItem
45943  * @extends Roo.util.Observable
45944  * Represents an individual item (tab plus body) in a TabPanel.
45945  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45946  * @param {String} id The id of this TabPanelItem
45947  * @param {String} text The text for the tab of this TabPanelItem
45948  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45949  */
45950 Roo.bootstrap.panel.TabItem = function(config){
45951     /**
45952      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45953      * @type Roo.TabPanel
45954      */
45955     this.tabPanel = config.panel;
45956     /**
45957      * The id for this TabPanelItem
45958      * @type String
45959      */
45960     this.id = config.id;
45961     /** @private */
45962     this.disabled = false;
45963     /** @private */
45964     this.text = config.text;
45965     /** @private */
45966     this.loaded = false;
45967     this.closable = config.closable;
45968
45969     /**
45970      * The body element for this TabPanelItem.
45971      * @type Roo.Element
45972      */
45973     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45974     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45975     this.bodyEl.setStyle("display", "block");
45976     this.bodyEl.setStyle("zoom", "1");
45977     //this.hideAction();
45978
45979     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45980     /** @private */
45981     this.el = Roo.get(els.el);
45982     this.inner = Roo.get(els.inner, true);
45983      this.textEl = Roo.bootstrap.version == 4 ?
45984         this.el : Roo.get(this.el.dom.firstChild, true);
45985
45986     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45987     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45988
45989     
45990 //    this.el.on("mousedown", this.onTabMouseDown, this);
45991     this.el.on("click", this.onTabClick, this);
45992     /** @private */
45993     if(config.closable){
45994         var c = Roo.get(els.close, true);
45995         c.dom.title = this.closeText;
45996         c.addClassOnOver("close-over");
45997         c.on("click", this.closeClick, this);
45998      }
45999
46000     this.addEvents({
46001          /**
46002          * @event activate
46003          * Fires when this tab becomes the active tab.
46004          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46005          * @param {Roo.TabPanelItem} this
46006          */
46007         "activate": true,
46008         /**
46009          * @event beforeclose
46010          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46011          * @param {Roo.TabPanelItem} this
46012          * @param {Object} e Set cancel to true on this object to cancel the close.
46013          */
46014         "beforeclose": true,
46015         /**
46016          * @event close
46017          * Fires when this tab is closed.
46018          * @param {Roo.TabPanelItem} this
46019          */
46020          "close": true,
46021         /**
46022          * @event deactivate
46023          * Fires when this tab is no longer the active tab.
46024          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46025          * @param {Roo.TabPanelItem} this
46026          */
46027          "deactivate" : true
46028     });
46029     this.hidden = false;
46030
46031     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46032 };
46033
46034 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46035            {
46036     purgeListeners : function(){
46037        Roo.util.Observable.prototype.purgeListeners.call(this);
46038        this.el.removeAllListeners();
46039     },
46040     /**
46041      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46042      */
46043     show : function(){
46044         this.status_node.addClass("active");
46045         this.showAction();
46046         if(Roo.isOpera){
46047             this.tabPanel.stripWrap.repaint();
46048         }
46049         this.fireEvent("activate", this.tabPanel, this);
46050     },
46051
46052     /**
46053      * Returns true if this tab is the active tab.
46054      * @return {Boolean}
46055      */
46056     isActive : function(){
46057         return this.tabPanel.getActiveTab() == this;
46058     },
46059
46060     /**
46061      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46062      */
46063     hide : function(){
46064         this.status_node.removeClass("active");
46065         this.hideAction();
46066         this.fireEvent("deactivate", this.tabPanel, this);
46067     },
46068
46069     hideAction : function(){
46070         this.bodyEl.hide();
46071         this.bodyEl.setStyle("position", "absolute");
46072         this.bodyEl.setLeft("-20000px");
46073         this.bodyEl.setTop("-20000px");
46074     },
46075
46076     showAction : function(){
46077         this.bodyEl.setStyle("position", "relative");
46078         this.bodyEl.setTop("");
46079         this.bodyEl.setLeft("");
46080         this.bodyEl.show();
46081     },
46082
46083     /**
46084      * Set the tooltip for the tab.
46085      * @param {String} tooltip The tab's tooltip
46086      */
46087     setTooltip : function(text){
46088         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46089             this.textEl.dom.qtip = text;
46090             this.textEl.dom.removeAttribute('title');
46091         }else{
46092             this.textEl.dom.title = text;
46093         }
46094     },
46095
46096     onTabClick : function(e){
46097         e.preventDefault();
46098         this.tabPanel.activate(this.id);
46099     },
46100
46101     onTabMouseDown : function(e){
46102         e.preventDefault();
46103         this.tabPanel.activate(this.id);
46104     },
46105 /*
46106     getWidth : function(){
46107         return this.inner.getWidth();
46108     },
46109
46110     setWidth : function(width){
46111         var iwidth = width - this.linode.getPadding("lr");
46112         this.inner.setWidth(iwidth);
46113         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46114         this.linode.setWidth(width);
46115     },
46116 */
46117     /**
46118      * Show or hide the tab
46119      * @param {Boolean} hidden True to hide or false to show.
46120      */
46121     setHidden : function(hidden){
46122         this.hidden = hidden;
46123         this.linode.setStyle("display", hidden ? "none" : "");
46124     },
46125
46126     /**
46127      * Returns true if this tab is "hidden"
46128      * @return {Boolean}
46129      */
46130     isHidden : function(){
46131         return this.hidden;
46132     },
46133
46134     /**
46135      * Returns the text for this tab
46136      * @return {String}
46137      */
46138     getText : function(){
46139         return this.text;
46140     },
46141     /*
46142     autoSize : function(){
46143         //this.el.beginMeasure();
46144         this.textEl.setWidth(1);
46145         /*
46146          *  #2804 [new] Tabs in Roojs
46147          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46148          */
46149         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46150         //this.el.endMeasure();
46151     //},
46152
46153     /**
46154      * Sets the text for the tab (Note: this also sets the tooltip text)
46155      * @param {String} text The tab's text and tooltip
46156      */
46157     setText : function(text){
46158         this.text = text;
46159         this.textEl.update(text);
46160         this.setTooltip(text);
46161         //if(!this.tabPanel.resizeTabs){
46162         //    this.autoSize();
46163         //}
46164     },
46165     /**
46166      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46167      */
46168     activate : function(){
46169         this.tabPanel.activate(this.id);
46170     },
46171
46172     /**
46173      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46174      */
46175     disable : function(){
46176         if(this.tabPanel.active != this){
46177             this.disabled = true;
46178             this.status_node.addClass("disabled");
46179         }
46180     },
46181
46182     /**
46183      * Enables this TabPanelItem if it was previously disabled.
46184      */
46185     enable : function(){
46186         this.disabled = false;
46187         this.status_node.removeClass("disabled");
46188     },
46189
46190     /**
46191      * Sets the content for this TabPanelItem.
46192      * @param {String} content The content
46193      * @param {Boolean} loadScripts true to look for and load scripts
46194      */
46195     setContent : function(content, loadScripts){
46196         this.bodyEl.update(content, loadScripts);
46197     },
46198
46199     /**
46200      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46201      * @return {Roo.UpdateManager} The UpdateManager
46202      */
46203     getUpdateManager : function(){
46204         return this.bodyEl.getUpdateManager();
46205     },
46206
46207     /**
46208      * Set a URL to be used to load the content for this TabPanelItem.
46209      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46210      * @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)
46211      * @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)
46212      * @return {Roo.UpdateManager} The UpdateManager
46213      */
46214     setUrl : function(url, params, loadOnce){
46215         if(this.refreshDelegate){
46216             this.un('activate', this.refreshDelegate);
46217         }
46218         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46219         this.on("activate", this.refreshDelegate);
46220         return this.bodyEl.getUpdateManager();
46221     },
46222
46223     /** @private */
46224     _handleRefresh : function(url, params, loadOnce){
46225         if(!loadOnce || !this.loaded){
46226             var updater = this.bodyEl.getUpdateManager();
46227             updater.update(url, params, this._setLoaded.createDelegate(this));
46228         }
46229     },
46230
46231     /**
46232      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46233      *   Will fail silently if the setUrl method has not been called.
46234      *   This does not activate the panel, just updates its content.
46235      */
46236     refresh : function(){
46237         if(this.refreshDelegate){
46238            this.loaded = false;
46239            this.refreshDelegate();
46240         }
46241     },
46242
46243     /** @private */
46244     _setLoaded : function(){
46245         this.loaded = true;
46246     },
46247
46248     /** @private */
46249     closeClick : function(e){
46250         var o = {};
46251         e.stopEvent();
46252         this.fireEvent("beforeclose", this, o);
46253         if(o.cancel !== true){
46254             this.tabPanel.removeTab(this.id);
46255         }
46256     },
46257     /**
46258      * The text displayed in the tooltip for the close icon.
46259      * @type String
46260      */
46261     closeText : "Close this tab"
46262 });
46263 /**
46264 *    This script refer to:
46265 *    Title: International Telephone Input
46266 *    Author: Jack O'Connor
46267 *    Code version:  v12.1.12
46268 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46269 **/
46270
46271 Roo.bootstrap.form.PhoneInputData = function() {
46272     var d = [
46273       [
46274         "Afghanistan (‫افغانستان‬‎)",
46275         "af",
46276         "93"
46277       ],
46278       [
46279         "Albania (Shqipëri)",
46280         "al",
46281         "355"
46282       ],
46283       [
46284         "Algeria (‫الجزائر‬‎)",
46285         "dz",
46286         "213"
46287       ],
46288       [
46289         "American Samoa",
46290         "as",
46291         "1684"
46292       ],
46293       [
46294         "Andorra",
46295         "ad",
46296         "376"
46297       ],
46298       [
46299         "Angola",
46300         "ao",
46301         "244"
46302       ],
46303       [
46304         "Anguilla",
46305         "ai",
46306         "1264"
46307       ],
46308       [
46309         "Antigua and Barbuda",
46310         "ag",
46311         "1268"
46312       ],
46313       [
46314         "Argentina",
46315         "ar",
46316         "54"
46317       ],
46318       [
46319         "Armenia (Հայաստան)",
46320         "am",
46321         "374"
46322       ],
46323       [
46324         "Aruba",
46325         "aw",
46326         "297"
46327       ],
46328       [
46329         "Australia",
46330         "au",
46331         "61",
46332         0
46333       ],
46334       [
46335         "Austria (Österreich)",
46336         "at",
46337         "43"
46338       ],
46339       [
46340         "Azerbaijan (Azərbaycan)",
46341         "az",
46342         "994"
46343       ],
46344       [
46345         "Bahamas",
46346         "bs",
46347         "1242"
46348       ],
46349       [
46350         "Bahrain (‫البحرين‬‎)",
46351         "bh",
46352         "973"
46353       ],
46354       [
46355         "Bangladesh (বাংলাদেশ)",
46356         "bd",
46357         "880"
46358       ],
46359       [
46360         "Barbados",
46361         "bb",
46362         "1246"
46363       ],
46364       [
46365         "Belarus (Беларусь)",
46366         "by",
46367         "375"
46368       ],
46369       [
46370         "Belgium (België)",
46371         "be",
46372         "32"
46373       ],
46374       [
46375         "Belize",
46376         "bz",
46377         "501"
46378       ],
46379       [
46380         "Benin (Bénin)",
46381         "bj",
46382         "229"
46383       ],
46384       [
46385         "Bermuda",
46386         "bm",
46387         "1441"
46388       ],
46389       [
46390         "Bhutan (འབྲུག)",
46391         "bt",
46392         "975"
46393       ],
46394       [
46395         "Bolivia",
46396         "bo",
46397         "591"
46398       ],
46399       [
46400         "Bosnia and Herzegovina (Босна и Херцеговина)",
46401         "ba",
46402         "387"
46403       ],
46404       [
46405         "Botswana",
46406         "bw",
46407         "267"
46408       ],
46409       [
46410         "Brazil (Brasil)",
46411         "br",
46412         "55"
46413       ],
46414       [
46415         "British Indian Ocean Territory",
46416         "io",
46417         "246"
46418       ],
46419       [
46420         "British Virgin Islands",
46421         "vg",
46422         "1284"
46423       ],
46424       [
46425         "Brunei",
46426         "bn",
46427         "673"
46428       ],
46429       [
46430         "Bulgaria (България)",
46431         "bg",
46432         "359"
46433       ],
46434       [
46435         "Burkina Faso",
46436         "bf",
46437         "226"
46438       ],
46439       [
46440         "Burundi (Uburundi)",
46441         "bi",
46442         "257"
46443       ],
46444       [
46445         "Cambodia (កម្ពុជា)",
46446         "kh",
46447         "855"
46448       ],
46449       [
46450         "Cameroon (Cameroun)",
46451         "cm",
46452         "237"
46453       ],
46454       [
46455         "Canada",
46456         "ca",
46457         "1",
46458         1,
46459         ["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"]
46460       ],
46461       [
46462         "Cape Verde (Kabu Verdi)",
46463         "cv",
46464         "238"
46465       ],
46466       [
46467         "Caribbean Netherlands",
46468         "bq",
46469         "599",
46470         1
46471       ],
46472       [
46473         "Cayman Islands",
46474         "ky",
46475         "1345"
46476       ],
46477       [
46478         "Central African Republic (République centrafricaine)",
46479         "cf",
46480         "236"
46481       ],
46482       [
46483         "Chad (Tchad)",
46484         "td",
46485         "235"
46486       ],
46487       [
46488         "Chile",
46489         "cl",
46490         "56"
46491       ],
46492       [
46493         "China (中国)",
46494         "cn",
46495         "86"
46496       ],
46497       [
46498         "Christmas Island",
46499         "cx",
46500         "61",
46501         2
46502       ],
46503       [
46504         "Cocos (Keeling) Islands",
46505         "cc",
46506         "61",
46507         1
46508       ],
46509       [
46510         "Colombia",
46511         "co",
46512         "57"
46513       ],
46514       [
46515         "Comoros (‫جزر القمر‬‎)",
46516         "km",
46517         "269"
46518       ],
46519       [
46520         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46521         "cd",
46522         "243"
46523       ],
46524       [
46525         "Congo (Republic) (Congo-Brazzaville)",
46526         "cg",
46527         "242"
46528       ],
46529       [
46530         "Cook Islands",
46531         "ck",
46532         "682"
46533       ],
46534       [
46535         "Costa Rica",
46536         "cr",
46537         "506"
46538       ],
46539       [
46540         "Côte d’Ivoire",
46541         "ci",
46542         "225"
46543       ],
46544       [
46545         "Croatia (Hrvatska)",
46546         "hr",
46547         "385"
46548       ],
46549       [
46550         "Cuba",
46551         "cu",
46552         "53"
46553       ],
46554       [
46555         "Curaçao",
46556         "cw",
46557         "599",
46558         0
46559       ],
46560       [
46561         "Cyprus (Κύπρος)",
46562         "cy",
46563         "357"
46564       ],
46565       [
46566         "Czech Republic (Česká republika)",
46567         "cz",
46568         "420"
46569       ],
46570       [
46571         "Denmark (Danmark)",
46572         "dk",
46573         "45"
46574       ],
46575       [
46576         "Djibouti",
46577         "dj",
46578         "253"
46579       ],
46580       [
46581         "Dominica",
46582         "dm",
46583         "1767"
46584       ],
46585       [
46586         "Dominican Republic (República Dominicana)",
46587         "do",
46588         "1",
46589         2,
46590         ["809", "829", "849"]
46591       ],
46592       [
46593         "Ecuador",
46594         "ec",
46595         "593"
46596       ],
46597       [
46598         "Egypt (‫مصر‬‎)",
46599         "eg",
46600         "20"
46601       ],
46602       [
46603         "El Salvador",
46604         "sv",
46605         "503"
46606       ],
46607       [
46608         "Equatorial Guinea (Guinea Ecuatorial)",
46609         "gq",
46610         "240"
46611       ],
46612       [
46613         "Eritrea",
46614         "er",
46615         "291"
46616       ],
46617       [
46618         "Estonia (Eesti)",
46619         "ee",
46620         "372"
46621       ],
46622       [
46623         "Ethiopia",
46624         "et",
46625         "251"
46626       ],
46627       [
46628         "Falkland Islands (Islas Malvinas)",
46629         "fk",
46630         "500"
46631       ],
46632       [
46633         "Faroe Islands (Føroyar)",
46634         "fo",
46635         "298"
46636       ],
46637       [
46638         "Fiji",
46639         "fj",
46640         "679"
46641       ],
46642       [
46643         "Finland (Suomi)",
46644         "fi",
46645         "358",
46646         0
46647       ],
46648       [
46649         "France",
46650         "fr",
46651         "33"
46652       ],
46653       [
46654         "French Guiana (Guyane française)",
46655         "gf",
46656         "594"
46657       ],
46658       [
46659         "French Polynesia (Polynésie française)",
46660         "pf",
46661         "689"
46662       ],
46663       [
46664         "Gabon",
46665         "ga",
46666         "241"
46667       ],
46668       [
46669         "Gambia",
46670         "gm",
46671         "220"
46672       ],
46673       [
46674         "Georgia (საქართველო)",
46675         "ge",
46676         "995"
46677       ],
46678       [
46679         "Germany (Deutschland)",
46680         "de",
46681         "49"
46682       ],
46683       [
46684         "Ghana (Gaana)",
46685         "gh",
46686         "233"
46687       ],
46688       [
46689         "Gibraltar",
46690         "gi",
46691         "350"
46692       ],
46693       [
46694         "Greece (Ελλάδα)",
46695         "gr",
46696         "30"
46697       ],
46698       [
46699         "Greenland (Kalaallit Nunaat)",
46700         "gl",
46701         "299"
46702       ],
46703       [
46704         "Grenada",
46705         "gd",
46706         "1473"
46707       ],
46708       [
46709         "Guadeloupe",
46710         "gp",
46711         "590",
46712         0
46713       ],
46714       [
46715         "Guam",
46716         "gu",
46717         "1671"
46718       ],
46719       [
46720         "Guatemala",
46721         "gt",
46722         "502"
46723       ],
46724       [
46725         "Guernsey",
46726         "gg",
46727         "44",
46728         1
46729       ],
46730       [
46731         "Guinea (Guinée)",
46732         "gn",
46733         "224"
46734       ],
46735       [
46736         "Guinea-Bissau (Guiné Bissau)",
46737         "gw",
46738         "245"
46739       ],
46740       [
46741         "Guyana",
46742         "gy",
46743         "592"
46744       ],
46745       [
46746         "Haiti",
46747         "ht",
46748         "509"
46749       ],
46750       [
46751         "Honduras",
46752         "hn",
46753         "504"
46754       ],
46755       [
46756         "Hong Kong (香港)",
46757         "hk",
46758         "852"
46759       ],
46760       [
46761         "Hungary (Magyarország)",
46762         "hu",
46763         "36"
46764       ],
46765       [
46766         "Iceland (Ísland)",
46767         "is",
46768         "354"
46769       ],
46770       [
46771         "India (भारत)",
46772         "in",
46773         "91"
46774       ],
46775       [
46776         "Indonesia",
46777         "id",
46778         "62"
46779       ],
46780       [
46781         "Iran (‫ایران‬‎)",
46782         "ir",
46783         "98"
46784       ],
46785       [
46786         "Iraq (‫العراق‬‎)",
46787         "iq",
46788         "964"
46789       ],
46790       [
46791         "Ireland",
46792         "ie",
46793         "353"
46794       ],
46795       [
46796         "Isle of Man",
46797         "im",
46798         "44",
46799         2
46800       ],
46801       [
46802         "Israel (‫ישראל‬‎)",
46803         "il",
46804         "972"
46805       ],
46806       [
46807         "Italy (Italia)",
46808         "it",
46809         "39",
46810         0
46811       ],
46812       [
46813         "Jamaica",
46814         "jm",
46815         "1876"
46816       ],
46817       [
46818         "Japan (日本)",
46819         "jp",
46820         "81"
46821       ],
46822       [
46823         "Jersey",
46824         "je",
46825         "44",
46826         3
46827       ],
46828       [
46829         "Jordan (‫الأردن‬‎)",
46830         "jo",
46831         "962"
46832       ],
46833       [
46834         "Kazakhstan (Казахстан)",
46835         "kz",
46836         "7",
46837         1
46838       ],
46839       [
46840         "Kenya",
46841         "ke",
46842         "254"
46843       ],
46844       [
46845         "Kiribati",
46846         "ki",
46847         "686"
46848       ],
46849       [
46850         "Kosovo",
46851         "xk",
46852         "383"
46853       ],
46854       [
46855         "Kuwait (‫الكويت‬‎)",
46856         "kw",
46857         "965"
46858       ],
46859       [
46860         "Kyrgyzstan (Кыргызстан)",
46861         "kg",
46862         "996"
46863       ],
46864       [
46865         "Laos (ລາວ)",
46866         "la",
46867         "856"
46868       ],
46869       [
46870         "Latvia (Latvija)",
46871         "lv",
46872         "371"
46873       ],
46874       [
46875         "Lebanon (‫لبنان‬‎)",
46876         "lb",
46877         "961"
46878       ],
46879       [
46880         "Lesotho",
46881         "ls",
46882         "266"
46883       ],
46884       [
46885         "Liberia",
46886         "lr",
46887         "231"
46888       ],
46889       [
46890         "Libya (‫ليبيا‬‎)",
46891         "ly",
46892         "218"
46893       ],
46894       [
46895         "Liechtenstein",
46896         "li",
46897         "423"
46898       ],
46899       [
46900         "Lithuania (Lietuva)",
46901         "lt",
46902         "370"
46903       ],
46904       [
46905         "Luxembourg",
46906         "lu",
46907         "352"
46908       ],
46909       [
46910         "Macau (澳門)",
46911         "mo",
46912         "853"
46913       ],
46914       [
46915         "Macedonia (FYROM) (Македонија)",
46916         "mk",
46917         "389"
46918       ],
46919       [
46920         "Madagascar (Madagasikara)",
46921         "mg",
46922         "261"
46923       ],
46924       [
46925         "Malawi",
46926         "mw",
46927         "265"
46928       ],
46929       [
46930         "Malaysia",
46931         "my",
46932         "60"
46933       ],
46934       [
46935         "Maldives",
46936         "mv",
46937         "960"
46938       ],
46939       [
46940         "Mali",
46941         "ml",
46942         "223"
46943       ],
46944       [
46945         "Malta",
46946         "mt",
46947         "356"
46948       ],
46949       [
46950         "Marshall Islands",
46951         "mh",
46952         "692"
46953       ],
46954       [
46955         "Martinique",
46956         "mq",
46957         "596"
46958       ],
46959       [
46960         "Mauritania (‫موريتانيا‬‎)",
46961         "mr",
46962         "222"
46963       ],
46964       [
46965         "Mauritius (Moris)",
46966         "mu",
46967         "230"
46968       ],
46969       [
46970         "Mayotte",
46971         "yt",
46972         "262",
46973         1
46974       ],
46975       [
46976         "Mexico (México)",
46977         "mx",
46978         "52"
46979       ],
46980       [
46981         "Micronesia",
46982         "fm",
46983         "691"
46984       ],
46985       [
46986         "Moldova (Republica Moldova)",
46987         "md",
46988         "373"
46989       ],
46990       [
46991         "Monaco",
46992         "mc",
46993         "377"
46994       ],
46995       [
46996         "Mongolia (Монгол)",
46997         "mn",
46998         "976"
46999       ],
47000       [
47001         "Montenegro (Crna Gora)",
47002         "me",
47003         "382"
47004       ],
47005       [
47006         "Montserrat",
47007         "ms",
47008         "1664"
47009       ],
47010       [
47011         "Morocco (‫المغرب‬‎)",
47012         "ma",
47013         "212",
47014         0
47015       ],
47016       [
47017         "Mozambique (Moçambique)",
47018         "mz",
47019         "258"
47020       ],
47021       [
47022         "Myanmar (Burma) (မြန်မာ)",
47023         "mm",
47024         "95"
47025       ],
47026       [
47027         "Namibia (Namibië)",
47028         "na",
47029         "264"
47030       ],
47031       [
47032         "Nauru",
47033         "nr",
47034         "674"
47035       ],
47036       [
47037         "Nepal (नेपाल)",
47038         "np",
47039         "977"
47040       ],
47041       [
47042         "Netherlands (Nederland)",
47043         "nl",
47044         "31"
47045       ],
47046       [
47047         "New Caledonia (Nouvelle-Calédonie)",
47048         "nc",
47049         "687"
47050       ],
47051       [
47052         "New Zealand",
47053         "nz",
47054         "64"
47055       ],
47056       [
47057         "Nicaragua",
47058         "ni",
47059         "505"
47060       ],
47061       [
47062         "Niger (Nijar)",
47063         "ne",
47064         "227"
47065       ],
47066       [
47067         "Nigeria",
47068         "ng",
47069         "234"
47070       ],
47071       [
47072         "Niue",
47073         "nu",
47074         "683"
47075       ],
47076       [
47077         "Norfolk Island",
47078         "nf",
47079         "672"
47080       ],
47081       [
47082         "North Korea (조선 민주주의 인민 공화국)",
47083         "kp",
47084         "850"
47085       ],
47086       [
47087         "Northern Mariana Islands",
47088         "mp",
47089         "1670"
47090       ],
47091       [
47092         "Norway (Norge)",
47093         "no",
47094         "47",
47095         0
47096       ],
47097       [
47098         "Oman (‫عُمان‬‎)",
47099         "om",
47100         "968"
47101       ],
47102       [
47103         "Pakistan (‫پاکستان‬‎)",
47104         "pk",
47105         "92"
47106       ],
47107       [
47108         "Palau",
47109         "pw",
47110         "680"
47111       ],
47112       [
47113         "Palestine (‫فلسطين‬‎)",
47114         "ps",
47115         "970"
47116       ],
47117       [
47118         "Panama (Panamá)",
47119         "pa",
47120         "507"
47121       ],
47122       [
47123         "Papua New Guinea",
47124         "pg",
47125         "675"
47126       ],
47127       [
47128         "Paraguay",
47129         "py",
47130         "595"
47131       ],
47132       [
47133         "Peru (Perú)",
47134         "pe",
47135         "51"
47136       ],
47137       [
47138         "Philippines",
47139         "ph",
47140         "63"
47141       ],
47142       [
47143         "Poland (Polska)",
47144         "pl",
47145         "48"
47146       ],
47147       [
47148         "Portugal",
47149         "pt",
47150         "351"
47151       ],
47152       [
47153         "Puerto Rico",
47154         "pr",
47155         "1",
47156         3,
47157         ["787", "939"]
47158       ],
47159       [
47160         "Qatar (‫قطر‬‎)",
47161         "qa",
47162         "974"
47163       ],
47164       [
47165         "Réunion (La Réunion)",
47166         "re",
47167         "262",
47168         0
47169       ],
47170       [
47171         "Romania (România)",
47172         "ro",
47173         "40"
47174       ],
47175       [
47176         "Russia (Россия)",
47177         "ru",
47178         "7",
47179         0
47180       ],
47181       [
47182         "Rwanda",
47183         "rw",
47184         "250"
47185       ],
47186       [
47187         "Saint Barthélemy",
47188         "bl",
47189         "590",
47190         1
47191       ],
47192       [
47193         "Saint Helena",
47194         "sh",
47195         "290"
47196       ],
47197       [
47198         "Saint Kitts and Nevis",
47199         "kn",
47200         "1869"
47201       ],
47202       [
47203         "Saint Lucia",
47204         "lc",
47205         "1758"
47206       ],
47207       [
47208         "Saint Martin (Saint-Martin (partie française))",
47209         "mf",
47210         "590",
47211         2
47212       ],
47213       [
47214         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47215         "pm",
47216         "508"
47217       ],
47218       [
47219         "Saint Vincent and the Grenadines",
47220         "vc",
47221         "1784"
47222       ],
47223       [
47224         "Samoa",
47225         "ws",
47226         "685"
47227       ],
47228       [
47229         "San Marino",
47230         "sm",
47231         "378"
47232       ],
47233       [
47234         "São Tomé and Príncipe (São Tomé e Príncipe)",
47235         "st",
47236         "239"
47237       ],
47238       [
47239         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47240         "sa",
47241         "966"
47242       ],
47243       [
47244         "Senegal (Sénégal)",
47245         "sn",
47246         "221"
47247       ],
47248       [
47249         "Serbia (Србија)",
47250         "rs",
47251         "381"
47252       ],
47253       [
47254         "Seychelles",
47255         "sc",
47256         "248"
47257       ],
47258       [
47259         "Sierra Leone",
47260         "sl",
47261         "232"
47262       ],
47263       [
47264         "Singapore",
47265         "sg",
47266         "65"
47267       ],
47268       [
47269         "Sint Maarten",
47270         "sx",
47271         "1721"
47272       ],
47273       [
47274         "Slovakia (Slovensko)",
47275         "sk",
47276         "421"
47277       ],
47278       [
47279         "Slovenia (Slovenija)",
47280         "si",
47281         "386"
47282       ],
47283       [
47284         "Solomon Islands",
47285         "sb",
47286         "677"
47287       ],
47288       [
47289         "Somalia (Soomaaliya)",
47290         "so",
47291         "252"
47292       ],
47293       [
47294         "South Africa",
47295         "za",
47296         "27"
47297       ],
47298       [
47299         "South Korea (대한민국)",
47300         "kr",
47301         "82"
47302       ],
47303       [
47304         "South Sudan (‫جنوب السودان‬‎)",
47305         "ss",
47306         "211"
47307       ],
47308       [
47309         "Spain (España)",
47310         "es",
47311         "34"
47312       ],
47313       [
47314         "Sri Lanka (ශ්‍රී ලංකාව)",
47315         "lk",
47316         "94"
47317       ],
47318       [
47319         "Sudan (‫السودان‬‎)",
47320         "sd",
47321         "249"
47322       ],
47323       [
47324         "Suriname",
47325         "sr",
47326         "597"
47327       ],
47328       [
47329         "Svalbard and Jan Mayen",
47330         "sj",
47331         "47",
47332         1
47333       ],
47334       [
47335         "Swaziland",
47336         "sz",
47337         "268"
47338       ],
47339       [
47340         "Sweden (Sverige)",
47341         "se",
47342         "46"
47343       ],
47344       [
47345         "Switzerland (Schweiz)",
47346         "ch",
47347         "41"
47348       ],
47349       [
47350         "Syria (‫سوريا‬‎)",
47351         "sy",
47352         "963"
47353       ],
47354       [
47355         "Taiwan (台灣)",
47356         "tw",
47357         "886"
47358       ],
47359       [
47360         "Tajikistan",
47361         "tj",
47362         "992"
47363       ],
47364       [
47365         "Tanzania",
47366         "tz",
47367         "255"
47368       ],
47369       [
47370         "Thailand (ไทย)",
47371         "th",
47372         "66"
47373       ],
47374       [
47375         "Timor-Leste",
47376         "tl",
47377         "670"
47378       ],
47379       [
47380         "Togo",
47381         "tg",
47382         "228"
47383       ],
47384       [
47385         "Tokelau",
47386         "tk",
47387         "690"
47388       ],
47389       [
47390         "Tonga",
47391         "to",
47392         "676"
47393       ],
47394       [
47395         "Trinidad and Tobago",
47396         "tt",
47397         "1868"
47398       ],
47399       [
47400         "Tunisia (‫تونس‬‎)",
47401         "tn",
47402         "216"
47403       ],
47404       [
47405         "Turkey (Türkiye)",
47406         "tr",
47407         "90"
47408       ],
47409       [
47410         "Turkmenistan",
47411         "tm",
47412         "993"
47413       ],
47414       [
47415         "Turks and Caicos Islands",
47416         "tc",
47417         "1649"
47418       ],
47419       [
47420         "Tuvalu",
47421         "tv",
47422         "688"
47423       ],
47424       [
47425         "U.S. Virgin Islands",
47426         "vi",
47427         "1340"
47428       ],
47429       [
47430         "Uganda",
47431         "ug",
47432         "256"
47433       ],
47434       [
47435         "Ukraine (Україна)",
47436         "ua",
47437         "380"
47438       ],
47439       [
47440         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47441         "ae",
47442         "971"
47443       ],
47444       [
47445         "United Kingdom",
47446         "gb",
47447         "44",
47448         0
47449       ],
47450       [
47451         "United States",
47452         "us",
47453         "1",
47454         0
47455       ],
47456       [
47457         "Uruguay",
47458         "uy",
47459         "598"
47460       ],
47461       [
47462         "Uzbekistan (Oʻzbekiston)",
47463         "uz",
47464         "998"
47465       ],
47466       [
47467         "Vanuatu",
47468         "vu",
47469         "678"
47470       ],
47471       [
47472         "Vatican City (Città del Vaticano)",
47473         "va",
47474         "39",
47475         1
47476       ],
47477       [
47478         "Venezuela",
47479         "ve",
47480         "58"
47481       ],
47482       [
47483         "Vietnam (Việt Nam)",
47484         "vn",
47485         "84"
47486       ],
47487       [
47488         "Wallis and Futuna (Wallis-et-Futuna)",
47489         "wf",
47490         "681"
47491       ],
47492       [
47493         "Western Sahara (‫الصحراء الغربية‬‎)",
47494         "eh",
47495         "212",
47496         1
47497       ],
47498       [
47499         "Yemen (‫اليمن‬‎)",
47500         "ye",
47501         "967"
47502       ],
47503       [
47504         "Zambia",
47505         "zm",
47506         "260"
47507       ],
47508       [
47509         "Zimbabwe",
47510         "zw",
47511         "263"
47512       ],
47513       [
47514         "Åland Islands",
47515         "ax",
47516         "358",
47517         1
47518       ]
47519   ];
47520   
47521   return d;
47522 }/**
47523 *    This script refer to:
47524 *    Title: International Telephone Input
47525 *    Author: Jack O'Connor
47526 *    Code version:  v12.1.12
47527 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47528 **/
47529
47530 /**
47531  * @class Roo.bootstrap.form.PhoneInput
47532  * @extends Roo.bootstrap.form.TriggerField
47533  * An input with International dial-code selection
47534  
47535  * @cfg {String} defaultDialCode default '+852'
47536  * @cfg {Array} preferedCountries default []
47537   
47538  * @constructor
47539  * Create a new PhoneInput.
47540  * @param {Object} config Configuration options
47541  */
47542
47543 Roo.bootstrap.form.PhoneInput = function(config) {
47544     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47545 };
47546
47547 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47548         /**
47549         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47550         */
47551         listWidth: undefined,
47552         
47553         selectedClass: 'active',
47554         
47555         invalidClass : "has-warning",
47556         
47557         validClass: 'has-success',
47558         
47559         allowed: '0123456789',
47560         
47561         max_length: 15,
47562         
47563         /**
47564          * @cfg {String} defaultDialCode The default dial code when initializing the input
47565          */
47566         defaultDialCode: '+852',
47567         
47568         /**
47569          * @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
47570          */
47571         preferedCountries: false,
47572         
47573         getAutoCreate : function()
47574         {
47575             var data = Roo.bootstrap.form.PhoneInputData();
47576             var align = this.labelAlign || this.parentLabelAlign();
47577             var id = Roo.id();
47578             
47579             this.allCountries = [];
47580             this.dialCodeMapping = [];
47581             
47582             for (var i = 0; i < data.length; i++) {
47583               var c = data[i];
47584               this.allCountries[i] = {
47585                 name: c[0],
47586                 iso2: c[1],
47587                 dialCode: c[2],
47588                 priority: c[3] || 0,
47589                 areaCodes: c[4] || null
47590               };
47591               this.dialCodeMapping[c[2]] = {
47592                   name: c[0],
47593                   iso2: c[1],
47594                   priority: c[3] || 0,
47595                   areaCodes: c[4] || null
47596               };
47597             }
47598             
47599             var cfg = {
47600                 cls: 'form-group',
47601                 cn: []
47602             };
47603             
47604             var input =  {
47605                 tag: 'input',
47606                 id : id,
47607                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47608                 maxlength: this.max_length,
47609                 cls : 'form-control tel-input',
47610                 autocomplete: 'new-password'
47611             };
47612             
47613             var hiddenInput = {
47614                 tag: 'input',
47615                 type: 'hidden',
47616                 cls: 'hidden-tel-input'
47617             };
47618             
47619             if (this.name) {
47620                 hiddenInput.name = this.name;
47621             }
47622             
47623             if (this.disabled) {
47624                 input.disabled = true;
47625             }
47626             
47627             var flag_container = {
47628                 tag: 'div',
47629                 cls: 'flag-box',
47630                 cn: [
47631                     {
47632                         tag: 'div',
47633                         cls: 'flag'
47634                     },
47635                     {
47636                         tag: 'div',
47637                         cls: 'caret'
47638                     }
47639                 ]
47640             };
47641             
47642             var box = {
47643                 tag: 'div',
47644                 cls: this.hasFeedback ? 'has-feedback' : '',
47645                 cn: [
47646                     hiddenInput,
47647                     input,
47648                     {
47649                         tag: 'input',
47650                         cls: 'dial-code-holder',
47651                         disabled: true
47652                     }
47653                 ]
47654             };
47655             
47656             var container = {
47657                 cls: 'roo-select2-container input-group',
47658                 cn: [
47659                     flag_container,
47660                     box
47661                 ]
47662             };
47663             
47664             if (this.fieldLabel.length) {
47665                 var indicator = {
47666                     tag: 'i',
47667                     tooltip: 'This field is required'
47668                 };
47669                 
47670                 var label = {
47671                     tag: 'label',
47672                     'for':  id,
47673                     cls: 'control-label',
47674                     cn: []
47675                 };
47676                 
47677                 var label_text = {
47678                     tag: 'span',
47679                     html: this.fieldLabel
47680                 };
47681                 
47682                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47683                 label.cn = [
47684                     indicator,
47685                     label_text
47686                 ];
47687                 
47688                 if(this.indicatorpos == 'right') {
47689                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47690                     label.cn = [
47691                         label_text,
47692                         indicator
47693                     ];
47694                 }
47695                 
47696                 if(align == 'left') {
47697                     container = {
47698                         tag: 'div',
47699                         cn: [
47700                             container
47701                         ]
47702                     };
47703                     
47704                     if(this.labelWidth > 12){
47705                         label.style = "width: " + this.labelWidth + 'px';
47706                     }
47707                     if(this.labelWidth < 13 && this.labelmd == 0){
47708                         this.labelmd = this.labelWidth;
47709                     }
47710                     if(this.labellg > 0){
47711                         label.cls += ' col-lg-' + this.labellg;
47712                         input.cls += ' col-lg-' + (12 - this.labellg);
47713                     }
47714                     if(this.labelmd > 0){
47715                         label.cls += ' col-md-' + this.labelmd;
47716                         container.cls += ' col-md-' + (12 - this.labelmd);
47717                     }
47718                     if(this.labelsm > 0){
47719                         label.cls += ' col-sm-' + this.labelsm;
47720                         container.cls += ' col-sm-' + (12 - this.labelsm);
47721                     }
47722                     if(this.labelxs > 0){
47723                         label.cls += ' col-xs-' + this.labelxs;
47724                         container.cls += ' col-xs-' + (12 - this.labelxs);
47725                     }
47726                 }
47727             }
47728             
47729             cfg.cn = [
47730                 label,
47731                 container
47732             ];
47733             
47734             var settings = this;
47735             
47736             ['xs','sm','md','lg'].map(function(size){
47737                 if (settings[size]) {
47738                     cfg.cls += ' col-' + size + '-' + settings[size];
47739                 }
47740             });
47741             
47742             this.store = new Roo.data.Store({
47743                 proxy : new Roo.data.MemoryProxy({}),
47744                 reader : new Roo.data.JsonReader({
47745                     fields : [
47746                         {
47747                             'name' : 'name',
47748                             'type' : 'string'
47749                         },
47750                         {
47751                             'name' : 'iso2',
47752                             'type' : 'string'
47753                         },
47754                         {
47755                             'name' : 'dialCode',
47756                             'type' : 'string'
47757                         },
47758                         {
47759                             'name' : 'priority',
47760                             'type' : 'string'
47761                         },
47762                         {
47763                             'name' : 'areaCodes',
47764                             'type' : 'string'
47765                         }
47766                     ]
47767                 })
47768             });
47769             
47770             if(!this.preferedCountries) {
47771                 this.preferedCountries = [
47772                     'hk',
47773                     'gb',
47774                     'us'
47775                 ];
47776             }
47777             
47778             var p = this.preferedCountries.reverse();
47779             
47780             if(p) {
47781                 for (var i = 0; i < p.length; i++) {
47782                     for (var j = 0; j < this.allCountries.length; j++) {
47783                         if(this.allCountries[j].iso2 == p[i]) {
47784                             var t = this.allCountries[j];
47785                             this.allCountries.splice(j,1);
47786                             this.allCountries.unshift(t);
47787                         }
47788                     } 
47789                 }
47790             }
47791             
47792             this.store.proxy.data = {
47793                 success: true,
47794                 data: this.allCountries
47795             };
47796             
47797             return cfg;
47798         },
47799         
47800         initEvents : function()
47801         {
47802             this.createList();
47803             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47804             
47805             this.indicator = this.indicatorEl();
47806             this.flag = this.flagEl();
47807             this.dialCodeHolder = this.dialCodeHolderEl();
47808             
47809             this.trigger = this.el.select('div.flag-box',true).first();
47810             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47811             
47812             var _this = this;
47813             
47814             (function(){
47815                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47816                 _this.list.setWidth(lw);
47817             }).defer(100);
47818             
47819             this.list.on('mouseover', this.onViewOver, this);
47820             this.list.on('mousemove', this.onViewMove, this);
47821             this.inputEl().on("keyup", this.onKeyUp, this);
47822             this.inputEl().on("keypress", this.onKeyPress, this);
47823             
47824             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47825
47826             this.view = new Roo.View(this.list, this.tpl, {
47827                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47828             });
47829             
47830             this.view.on('click', this.onViewClick, this);
47831             this.setValue(this.defaultDialCode);
47832         },
47833         
47834         onTriggerClick : function(e)
47835         {
47836             Roo.log('trigger click');
47837             if(this.disabled){
47838                 return;
47839             }
47840             
47841             if(this.isExpanded()){
47842                 this.collapse();
47843                 this.hasFocus = false;
47844             }else {
47845                 this.store.load({});
47846                 this.hasFocus = true;
47847                 this.expand();
47848             }
47849         },
47850         
47851         isExpanded : function()
47852         {
47853             return this.list.isVisible();
47854         },
47855         
47856         collapse : function()
47857         {
47858             if(!this.isExpanded()){
47859                 return;
47860             }
47861             this.list.hide();
47862             Roo.get(document).un('mousedown', this.collapseIf, this);
47863             Roo.get(document).un('mousewheel', this.collapseIf, this);
47864             this.fireEvent('collapse', this);
47865             this.validate();
47866         },
47867         
47868         expand : function()
47869         {
47870             Roo.log('expand');
47871
47872             if(this.isExpanded() || !this.hasFocus){
47873                 return;
47874             }
47875             
47876             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47877             this.list.setWidth(lw);
47878             
47879             this.list.show();
47880             this.restrictHeight();
47881             
47882             Roo.get(document).on('mousedown', this.collapseIf, this);
47883             Roo.get(document).on('mousewheel', this.collapseIf, this);
47884             
47885             this.fireEvent('expand', this);
47886         },
47887         
47888         restrictHeight : function()
47889         {
47890             this.list.alignTo(this.inputEl(), this.listAlign);
47891             this.list.alignTo(this.inputEl(), this.listAlign);
47892         },
47893         
47894         onViewOver : function(e, t)
47895         {
47896             if(this.inKeyMode){
47897                 return;
47898             }
47899             var item = this.view.findItemFromChild(t);
47900             
47901             if(item){
47902                 var index = this.view.indexOf(item);
47903                 this.select(index, false);
47904             }
47905         },
47906
47907         // private
47908         onViewClick : function(view, doFocus, el, e)
47909         {
47910             var index = this.view.getSelectedIndexes()[0];
47911             
47912             var r = this.store.getAt(index);
47913             
47914             if(r){
47915                 this.onSelect(r, index);
47916             }
47917             if(doFocus !== false && !this.blockFocus){
47918                 this.inputEl().focus();
47919             }
47920         },
47921         
47922         onViewMove : function(e, t)
47923         {
47924             this.inKeyMode = false;
47925         },
47926         
47927         select : function(index, scrollIntoView)
47928         {
47929             this.selectedIndex = index;
47930             this.view.select(index);
47931             if(scrollIntoView !== false){
47932                 var el = this.view.getNode(index);
47933                 if(el){
47934                     this.list.scrollChildIntoView(el, false);
47935                 }
47936             }
47937         },
47938         
47939         createList : function()
47940         {
47941             this.list = Roo.get(document.body).createChild({
47942                 tag: 'ul',
47943                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47944                 style: 'display:none'
47945             });
47946             
47947             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47948         },
47949         
47950         collapseIf : function(e)
47951         {
47952             var in_combo  = e.within(this.el);
47953             var in_list =  e.within(this.list);
47954             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47955             
47956             if (in_combo || in_list || is_list) {
47957                 return;
47958             }
47959             this.collapse();
47960         },
47961         
47962         onSelect : function(record, index)
47963         {
47964             if(this.fireEvent('beforeselect', this, record, index) !== false){
47965                 
47966                 this.setFlagClass(record.data.iso2);
47967                 this.setDialCode(record.data.dialCode);
47968                 this.hasFocus = false;
47969                 this.collapse();
47970                 this.fireEvent('select', this, record, index);
47971             }
47972         },
47973         
47974         flagEl : function()
47975         {
47976             var flag = this.el.select('div.flag',true).first();
47977             if(!flag){
47978                 return false;
47979             }
47980             return flag;
47981         },
47982         
47983         dialCodeHolderEl : function()
47984         {
47985             var d = this.el.select('input.dial-code-holder',true).first();
47986             if(!d){
47987                 return false;
47988             }
47989             return d;
47990         },
47991         
47992         setDialCode : function(v)
47993         {
47994             this.dialCodeHolder.dom.value = '+'+v;
47995         },
47996         
47997         setFlagClass : function(n)
47998         {
47999             this.flag.dom.className = 'flag '+n;
48000         },
48001         
48002         getValue : function()
48003         {
48004             var v = this.inputEl().getValue();
48005             if(this.dialCodeHolder) {
48006                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48007             }
48008             return v;
48009         },
48010         
48011         setValue : function(v)
48012         {
48013             var d = this.getDialCode(v);
48014             
48015             //invalid dial code
48016             if(v.length == 0 || !d || d.length == 0) {
48017                 if(this.rendered){
48018                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48019                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48020                 }
48021                 return;
48022             }
48023             
48024             //valid dial code
48025             this.setFlagClass(this.dialCodeMapping[d].iso2);
48026             this.setDialCode(d);
48027             this.inputEl().dom.value = v.replace('+'+d,'');
48028             this.hiddenEl().dom.value = this.getValue();
48029             
48030             this.validate();
48031         },
48032         
48033         getDialCode : function(v)
48034         {
48035             v = v ||  '';
48036             
48037             if (v.length == 0) {
48038                 return this.dialCodeHolder.dom.value;
48039             }
48040             
48041             var dialCode = "";
48042             if (v.charAt(0) != "+") {
48043                 return false;
48044             }
48045             var numericChars = "";
48046             for (var i = 1; i < v.length; i++) {
48047               var c = v.charAt(i);
48048               if (!isNaN(c)) {
48049                 numericChars += c;
48050                 if (this.dialCodeMapping[numericChars]) {
48051                   dialCode = v.substr(1, i);
48052                 }
48053                 if (numericChars.length == 4) {
48054                   break;
48055                 }
48056               }
48057             }
48058             return dialCode;
48059         },
48060         
48061         reset : function()
48062         {
48063             this.setValue(this.defaultDialCode);
48064             this.validate();
48065         },
48066         
48067         hiddenEl : function()
48068         {
48069             return this.el.select('input.hidden-tel-input',true).first();
48070         },
48071         
48072         // after setting val
48073         onKeyUp : function(e){
48074             this.setValue(this.getValue());
48075         },
48076         
48077         onKeyPress : function(e){
48078             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48079                 e.stopEvent();
48080             }
48081         }
48082         
48083 });
48084 /**
48085  * @class Roo.bootstrap.form.MoneyField
48086  * @extends Roo.bootstrap.form.ComboBox
48087  * Bootstrap MoneyField class
48088  * 
48089  * @constructor
48090  * Create a new MoneyField.
48091  * @param {Object} config Configuration options
48092  */
48093
48094 Roo.bootstrap.form.MoneyField = function(config) {
48095     
48096     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48097     
48098 };
48099
48100 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48101     
48102     /**
48103      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48104      */
48105     allowDecimals : true,
48106     /**
48107      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48108      */
48109     decimalSeparator : ".",
48110     /**
48111      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48112      */
48113     decimalPrecision : 0,
48114     /**
48115      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48116      */
48117     allowNegative : true,
48118     /**
48119      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48120      */
48121     allowZero: true,
48122     /**
48123      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48124      */
48125     minValue : Number.NEGATIVE_INFINITY,
48126     /**
48127      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48128      */
48129     maxValue : Number.MAX_VALUE,
48130     /**
48131      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48132      */
48133     minText : "The minimum value for this field is {0}",
48134     /**
48135      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48136      */
48137     maxText : "The maximum value for this field is {0}",
48138     /**
48139      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48140      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48141      */
48142     nanText : "{0} is not a valid number",
48143     /**
48144      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48145      */
48146     castInt : true,
48147     /**
48148      * @cfg {String} defaults currency of the MoneyField
48149      * value should be in lkey
48150      */
48151     defaultCurrency : false,
48152     /**
48153      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48154      */
48155     thousandsDelimiter : false,
48156     /**
48157      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48158      */
48159     max_length: false,
48160     
48161     inputlg : 9,
48162     inputmd : 9,
48163     inputsm : 9,
48164     inputxs : 6,
48165      /**
48166      * @cfg {Roo.data.Store} store  Store to lookup currency??
48167      */
48168     store : false,
48169     
48170     getAutoCreate : function()
48171     {
48172         var align = this.labelAlign || this.parentLabelAlign();
48173         
48174         var id = Roo.id();
48175
48176         var cfg = {
48177             cls: 'form-group',
48178             cn: []
48179         };
48180
48181         var input =  {
48182             tag: 'input',
48183             id : id,
48184             cls : 'form-control roo-money-amount-input',
48185             autocomplete: 'new-password'
48186         };
48187         
48188         var hiddenInput = {
48189             tag: 'input',
48190             type: 'hidden',
48191             id: Roo.id(),
48192             cls: 'hidden-number-input'
48193         };
48194         
48195         if(this.max_length) {
48196             input.maxlength = this.max_length; 
48197         }
48198         
48199         if (this.name) {
48200             hiddenInput.name = this.name;
48201         }
48202
48203         if (this.disabled) {
48204             input.disabled = true;
48205         }
48206
48207         var clg = 12 - this.inputlg;
48208         var cmd = 12 - this.inputmd;
48209         var csm = 12 - this.inputsm;
48210         var cxs = 12 - this.inputxs;
48211         
48212         var container = {
48213             tag : 'div',
48214             cls : 'row roo-money-field',
48215             cn : [
48216                 {
48217                     tag : 'div',
48218                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48219                     cn : [
48220                         {
48221                             tag : 'div',
48222                             cls: 'roo-select2-container input-group',
48223                             cn: [
48224                                 {
48225                                     tag : 'input',
48226                                     cls : 'form-control roo-money-currency-input',
48227                                     autocomplete: 'new-password',
48228                                     readOnly : 1,
48229                                     name : this.currencyName
48230                                 },
48231                                 {
48232                                     tag :'span',
48233                                     cls : 'input-group-addon',
48234                                     cn : [
48235                                         {
48236                                             tag: 'span',
48237                                             cls: 'caret'
48238                                         }
48239                                     ]
48240                                 }
48241                             ]
48242                         }
48243                     ]
48244                 },
48245                 {
48246                     tag : 'div',
48247                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48248                     cn : [
48249                         {
48250                             tag: 'div',
48251                             cls: this.hasFeedback ? 'has-feedback' : '',
48252                             cn: [
48253                                 input
48254                             ]
48255                         }
48256                     ]
48257                 }
48258             ]
48259             
48260         };
48261         
48262         if (this.fieldLabel.length) {
48263             var indicator = {
48264                 tag: 'i',
48265                 tooltip: 'This field is required'
48266             };
48267
48268             var label = {
48269                 tag: 'label',
48270                 'for':  id,
48271                 cls: 'control-label',
48272                 cn: []
48273             };
48274
48275             var label_text = {
48276                 tag: 'span',
48277                 html: this.fieldLabel
48278             };
48279
48280             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48281             label.cn = [
48282                 indicator,
48283                 label_text
48284             ];
48285
48286             if(this.indicatorpos == 'right') {
48287                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48288                 label.cn = [
48289                     label_text,
48290                     indicator
48291                 ];
48292             }
48293
48294             if(align == 'left') {
48295                 container = {
48296                     tag: 'div',
48297                     cn: [
48298                         container
48299                     ]
48300                 };
48301
48302                 if(this.labelWidth > 12){
48303                     label.style = "width: " + this.labelWidth + 'px';
48304                 }
48305                 if(this.labelWidth < 13 && this.labelmd == 0){
48306                     this.labelmd = this.labelWidth;
48307                 }
48308                 if(this.labellg > 0){
48309                     label.cls += ' col-lg-' + this.labellg;
48310                     input.cls += ' col-lg-' + (12 - this.labellg);
48311                 }
48312                 if(this.labelmd > 0){
48313                     label.cls += ' col-md-' + this.labelmd;
48314                     container.cls += ' col-md-' + (12 - this.labelmd);
48315                 }
48316                 if(this.labelsm > 0){
48317                     label.cls += ' col-sm-' + this.labelsm;
48318                     container.cls += ' col-sm-' + (12 - this.labelsm);
48319                 }
48320                 if(this.labelxs > 0){
48321                     label.cls += ' col-xs-' + this.labelxs;
48322                     container.cls += ' col-xs-' + (12 - this.labelxs);
48323                 }
48324             }
48325         }
48326
48327         cfg.cn = [
48328             label,
48329             container,
48330             hiddenInput
48331         ];
48332         
48333         var settings = this;
48334
48335         ['xs','sm','md','lg'].map(function(size){
48336             if (settings[size]) {
48337                 cfg.cls += ' col-' + size + '-' + settings[size];
48338             }
48339         });
48340         
48341         return cfg;
48342     },
48343     
48344     initEvents : function()
48345     {
48346         this.indicator = this.indicatorEl();
48347         
48348         this.initCurrencyEvent();
48349         
48350         this.initNumberEvent();
48351     },
48352     
48353     initCurrencyEvent : function()
48354     {
48355         if (!this.store) {
48356             throw "can not find store for combo";
48357         }
48358         
48359         this.store = Roo.factory(this.store, Roo.data);
48360         this.store.parent = this;
48361         
48362         this.createList();
48363         
48364         this.triggerEl = this.el.select('.input-group-addon', true).first();
48365         
48366         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48367         
48368         var _this = this;
48369         
48370         (function(){
48371             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48372             _this.list.setWidth(lw);
48373         }).defer(100);
48374         
48375         this.list.on('mouseover', this.onViewOver, this);
48376         this.list.on('mousemove', this.onViewMove, this);
48377         this.list.on('scroll', this.onViewScroll, this);
48378         
48379         if(!this.tpl){
48380             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48381         }
48382         
48383         this.view = new Roo.View(this.list, this.tpl, {
48384             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48385         });
48386         
48387         this.view.on('click', this.onViewClick, this);
48388         
48389         this.store.on('beforeload', this.onBeforeLoad, this);
48390         this.store.on('load', this.onLoad, this);
48391         this.store.on('loadexception', this.onLoadException, this);
48392         
48393         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48394             "up" : function(e){
48395                 this.inKeyMode = true;
48396                 this.selectPrev();
48397             },
48398
48399             "down" : function(e){
48400                 if(!this.isExpanded()){
48401                     this.onTriggerClick();
48402                 }else{
48403                     this.inKeyMode = true;
48404                     this.selectNext();
48405                 }
48406             },
48407
48408             "enter" : function(e){
48409                 this.collapse();
48410                 
48411                 if(this.fireEvent("specialkey", this, e)){
48412                     this.onViewClick(false);
48413                 }
48414                 
48415                 return true;
48416             },
48417
48418             "esc" : function(e){
48419                 this.collapse();
48420             },
48421
48422             "tab" : function(e){
48423                 this.collapse();
48424                 
48425                 if(this.fireEvent("specialkey", this, e)){
48426                     this.onViewClick(false);
48427                 }
48428                 
48429                 return true;
48430             },
48431
48432             scope : this,
48433
48434             doRelay : function(foo, bar, hname){
48435                 if(hname == 'down' || this.scope.isExpanded()){
48436                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48437                 }
48438                 return true;
48439             },
48440
48441             forceKeyDown: true
48442         });
48443         
48444         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48445         
48446     },
48447     
48448     initNumberEvent : function(e)
48449     {
48450         this.inputEl().on("keydown" , this.fireKey,  this);
48451         this.inputEl().on("focus", this.onFocus,  this);
48452         this.inputEl().on("blur", this.onBlur,  this);
48453         
48454         this.inputEl().relayEvent('keyup', this);
48455         
48456         if(this.indicator){
48457             this.indicator.addClass('invisible');
48458         }
48459  
48460         this.originalValue = this.getValue();
48461         
48462         if(this.validationEvent == 'keyup'){
48463             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48464             this.inputEl().on('keyup', this.filterValidation, this);
48465         }
48466         else if(this.validationEvent !== false){
48467             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48468         }
48469         
48470         if(this.selectOnFocus){
48471             this.on("focus", this.preFocus, this);
48472             
48473         }
48474         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48475             this.inputEl().on("keypress", this.filterKeys, this);
48476         } else {
48477             this.inputEl().relayEvent('keypress', this);
48478         }
48479         
48480         var allowed = "0123456789";
48481         
48482         if(this.allowDecimals){
48483             allowed += this.decimalSeparator;
48484         }
48485         
48486         if(this.allowNegative){
48487             allowed += "-";
48488         }
48489         
48490         if(this.thousandsDelimiter) {
48491             allowed += ",";
48492         }
48493         
48494         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48495         
48496         var keyPress = function(e){
48497             
48498             var k = e.getKey();
48499             
48500             var c = e.getCharCode();
48501             
48502             if(
48503                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48504                     allowed.indexOf(String.fromCharCode(c)) === -1
48505             ){
48506                 e.stopEvent();
48507                 return;
48508             }
48509             
48510             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48511                 return;
48512             }
48513             
48514             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48515                 e.stopEvent();
48516             }
48517         };
48518         
48519         this.inputEl().on("keypress", keyPress, this);
48520         
48521     },
48522     
48523     onTriggerClick : function(e)
48524     {   
48525         if(this.disabled){
48526             return;
48527         }
48528         
48529         this.page = 0;
48530         this.loadNext = false;
48531         
48532         if(this.isExpanded()){
48533             this.collapse();
48534             return;
48535         }
48536         
48537         this.hasFocus = true;
48538         
48539         if(this.triggerAction == 'all') {
48540             this.doQuery(this.allQuery, true);
48541             return;
48542         }
48543         
48544         this.doQuery(this.getRawValue());
48545     },
48546     
48547     getCurrency : function()
48548     {   
48549         var v = this.currencyEl().getValue();
48550         
48551         return v;
48552     },
48553     
48554     restrictHeight : function()
48555     {
48556         this.list.alignTo(this.currencyEl(), this.listAlign);
48557         this.list.alignTo(this.currencyEl(), this.listAlign);
48558     },
48559     
48560     onViewClick : function(view, doFocus, el, e)
48561     {
48562         var index = this.view.getSelectedIndexes()[0];
48563         
48564         var r = this.store.getAt(index);
48565         
48566         if(r){
48567             this.onSelect(r, index);
48568         }
48569     },
48570     
48571     onSelect : function(record, index){
48572         
48573         if(this.fireEvent('beforeselect', this, record, index) !== false){
48574         
48575             this.setFromCurrencyData(index > -1 ? record.data : false);
48576             
48577             this.collapse();
48578             
48579             this.fireEvent('select', this, record, index);
48580         }
48581     },
48582     
48583     setFromCurrencyData : function(o)
48584     {
48585         var currency = '';
48586         
48587         this.lastCurrency = o;
48588         
48589         if (this.currencyField) {
48590             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48591         } else {
48592             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48593         }
48594         
48595         this.lastSelectionText = currency;
48596         
48597         //setting default currency
48598         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48599             this.setCurrency(this.defaultCurrency);
48600             return;
48601         }
48602         
48603         this.setCurrency(currency);
48604     },
48605     
48606     setFromData : function(o)
48607     {
48608         var c = {};
48609         
48610         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48611         
48612         this.setFromCurrencyData(c);
48613         
48614         var value = '';
48615         
48616         if (this.name) {
48617             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48618         } else {
48619             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48620         }
48621         
48622         this.setValue(value);
48623         
48624     },
48625     
48626     setCurrency : function(v)
48627     {   
48628         this.currencyValue = v;
48629         
48630         if(this.rendered){
48631             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48632             this.validate();
48633         }
48634     },
48635     
48636     setValue : function(v)
48637     {
48638         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48639         
48640         this.value = v;
48641         
48642         if(this.rendered){
48643             
48644             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48645             
48646             this.inputEl().dom.value = (v == '') ? '' :
48647                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48648             
48649             if(!this.allowZero && v === '0') {
48650                 this.hiddenEl().dom.value = '';
48651                 this.inputEl().dom.value = '';
48652             }
48653             
48654             this.validate();
48655         }
48656     },
48657     
48658     getRawValue : function()
48659     {
48660         var v = this.inputEl().getValue();
48661         
48662         return v;
48663     },
48664     
48665     getValue : function()
48666     {
48667         return this.fixPrecision(this.parseValue(this.getRawValue()));
48668     },
48669     
48670     parseValue : function(value)
48671     {
48672         if(this.thousandsDelimiter) {
48673             value += "";
48674             r = new RegExp(",", "g");
48675             value = value.replace(r, "");
48676         }
48677         
48678         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48679         return isNaN(value) ? '' : value;
48680         
48681     },
48682     
48683     fixPrecision : function(value)
48684     {
48685         if(this.thousandsDelimiter) {
48686             value += "";
48687             r = new RegExp(",", "g");
48688             value = value.replace(r, "");
48689         }
48690         
48691         var nan = isNaN(value);
48692         
48693         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48694             return nan ? '' : value;
48695         }
48696         return parseFloat(value).toFixed(this.decimalPrecision);
48697     },
48698     
48699     decimalPrecisionFcn : function(v)
48700     {
48701         return Math.floor(v);
48702     },
48703     
48704     validateValue : function(value)
48705     {
48706         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48707             return false;
48708         }
48709         
48710         var num = this.parseValue(value);
48711         
48712         if(isNaN(num)){
48713             this.markInvalid(String.format(this.nanText, value));
48714             return false;
48715         }
48716         
48717         if(num < this.minValue){
48718             this.markInvalid(String.format(this.minText, this.minValue));
48719             return false;
48720         }
48721         
48722         if(num > this.maxValue){
48723             this.markInvalid(String.format(this.maxText, this.maxValue));
48724             return false;
48725         }
48726         
48727         return true;
48728     },
48729     
48730     validate : function()
48731     {
48732         if(this.disabled || this.allowBlank){
48733             this.markValid();
48734             return true;
48735         }
48736         
48737         var currency = this.getCurrency();
48738         
48739         if(this.validateValue(this.getRawValue()) && currency.length){
48740             this.markValid();
48741             return true;
48742         }
48743         
48744         this.markInvalid();
48745         return false;
48746     },
48747     
48748     getName: function()
48749     {
48750         return this.name;
48751     },
48752     
48753     beforeBlur : function()
48754     {
48755         if(!this.castInt){
48756             return;
48757         }
48758         
48759         var v = this.parseValue(this.getRawValue());
48760         
48761         if(v || v == 0){
48762             this.setValue(v);
48763         }
48764     },
48765     
48766     onBlur : function()
48767     {
48768         this.beforeBlur();
48769         
48770         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48771             //this.el.removeClass(this.focusClass);
48772         }
48773         
48774         this.hasFocus = false;
48775         
48776         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48777             this.validate();
48778         }
48779         
48780         var v = this.getValue();
48781         
48782         if(String(v) !== String(this.startValue)){
48783             this.fireEvent('change', this, v, this.startValue);
48784         }
48785         
48786         this.fireEvent("blur", this);
48787     },
48788     
48789     inputEl : function()
48790     {
48791         return this.el.select('.roo-money-amount-input', true).first();
48792     },
48793     
48794     currencyEl : function()
48795     {
48796         return this.el.select('.roo-money-currency-input', true).first();
48797     },
48798     
48799     hiddenEl : function()
48800     {
48801         return this.el.select('input.hidden-number-input',true).first();
48802     }
48803     
48804 });/**
48805  * @class Roo.bootstrap.BezierSignature
48806  * @extends Roo.bootstrap.Component
48807  * Bootstrap BezierSignature class
48808  * This script refer to:
48809  *    Title: Signature Pad
48810  *    Author: szimek
48811  *    Availability: https://github.com/szimek/signature_pad
48812  *
48813  * @constructor
48814  * Create a new BezierSignature
48815  * @param {Object} config The config object
48816  */
48817
48818 Roo.bootstrap.BezierSignature = function(config){
48819     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48820     this.addEvents({
48821         "resize" : true
48822     });
48823 };
48824
48825 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48826 {
48827      
48828     curve_data: [],
48829     
48830     is_empty: true,
48831     
48832     mouse_btn_down: true,
48833     
48834     /**
48835      * @cfg {int} canvas height
48836      */
48837     canvas_height: '200px',
48838     
48839     /**
48840      * @cfg {float|function} Radius of a single dot.
48841      */ 
48842     dot_size: false,
48843     
48844     /**
48845      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48846      */
48847     min_width: 0.5,
48848     
48849     /**
48850      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48851      */
48852     max_width: 2.5,
48853     
48854     /**
48855      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48856      */
48857     throttle: 16,
48858     
48859     /**
48860      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48861      */
48862     min_distance: 5,
48863     
48864     /**
48865      * @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.
48866      */
48867     bg_color: 'rgba(0, 0, 0, 0)',
48868     
48869     /**
48870      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48871      */
48872     dot_color: 'black',
48873     
48874     /**
48875      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48876      */ 
48877     velocity_filter_weight: 0.7,
48878     
48879     /**
48880      * @cfg {function} Callback when stroke begin. 
48881      */
48882     onBegin: false,
48883     
48884     /**
48885      * @cfg {function} Callback when stroke end.
48886      */
48887     onEnd: false,
48888     
48889     getAutoCreate : function()
48890     {
48891         var cls = 'roo-signature column';
48892         
48893         if(this.cls){
48894             cls += ' ' + this.cls;
48895         }
48896         
48897         var col_sizes = [
48898             'lg',
48899             'md',
48900             'sm',
48901             'xs'
48902         ];
48903         
48904         for(var i = 0; i < col_sizes.length; i++) {
48905             if(this[col_sizes[i]]) {
48906                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48907             }
48908         }
48909         
48910         var cfg = {
48911             tag: 'div',
48912             cls: cls,
48913             cn: [
48914                 {
48915                     tag: 'div',
48916                     cls: 'roo-signature-body',
48917                     cn: [
48918                         {
48919                             tag: 'canvas',
48920                             cls: 'roo-signature-body-canvas',
48921                             height: this.canvas_height,
48922                             width: this.canvas_width
48923                         }
48924                     ]
48925                 },
48926                 {
48927                     tag: 'input',
48928                     type: 'file',
48929                     style: 'display: none'
48930                 }
48931             ]
48932         };
48933         
48934         return cfg;
48935     },
48936     
48937     initEvents: function() 
48938     {
48939         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48940         
48941         var canvas = this.canvasEl();
48942         
48943         // mouse && touch event swapping...
48944         canvas.dom.style.touchAction = 'none';
48945         canvas.dom.style.msTouchAction = 'none';
48946         
48947         this.mouse_btn_down = false;
48948         canvas.on('mousedown', this._handleMouseDown, this);
48949         canvas.on('mousemove', this._handleMouseMove, this);
48950         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48951         
48952         if (window.PointerEvent) {
48953             canvas.on('pointerdown', this._handleMouseDown, this);
48954             canvas.on('pointermove', this._handleMouseMove, this);
48955             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48956         }
48957         
48958         if ('ontouchstart' in window) {
48959             canvas.on('touchstart', this._handleTouchStart, this);
48960             canvas.on('touchmove', this._handleTouchMove, this);
48961             canvas.on('touchend', this._handleTouchEnd, this);
48962         }
48963         
48964         Roo.EventManager.onWindowResize(this.resize, this, true);
48965         
48966         // file input event
48967         this.fileEl().on('change', this.uploadImage, this);
48968         
48969         this.clear();
48970         
48971         this.resize();
48972     },
48973     
48974     resize: function(){
48975         
48976         var canvas = this.canvasEl().dom;
48977         var ctx = this.canvasElCtx();
48978         var img_data = false;
48979         
48980         if(canvas.width > 0) {
48981             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48982         }
48983         // setting canvas width will clean img data
48984         canvas.width = 0;
48985         
48986         var style = window.getComputedStyle ? 
48987             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48988             
48989         var padding_left = parseInt(style.paddingLeft) || 0;
48990         var padding_right = parseInt(style.paddingRight) || 0;
48991         
48992         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48993         
48994         if(img_data) {
48995             ctx.putImageData(img_data, 0, 0);
48996         }
48997     },
48998     
48999     _handleMouseDown: function(e)
49000     {
49001         if (e.browserEvent.which === 1) {
49002             this.mouse_btn_down = true;
49003             this.strokeBegin(e);
49004         }
49005     },
49006     
49007     _handleMouseMove: function (e)
49008     {
49009         if (this.mouse_btn_down) {
49010             this.strokeMoveUpdate(e);
49011         }
49012     },
49013     
49014     _handleMouseUp: function (e)
49015     {
49016         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49017             this.mouse_btn_down = false;
49018             this.strokeEnd(e);
49019         }
49020     },
49021     
49022     _handleTouchStart: function (e) {
49023         
49024         e.preventDefault();
49025         if (e.browserEvent.targetTouches.length === 1) {
49026             // var touch = e.browserEvent.changedTouches[0];
49027             // this.strokeBegin(touch);
49028             
49029              this.strokeBegin(e); // assume e catching the correct xy...
49030         }
49031     },
49032     
49033     _handleTouchMove: function (e) {
49034         e.preventDefault();
49035         // var touch = event.targetTouches[0];
49036         // _this._strokeMoveUpdate(touch);
49037         this.strokeMoveUpdate(e);
49038     },
49039     
49040     _handleTouchEnd: function (e) {
49041         var wasCanvasTouched = e.target === this.canvasEl().dom;
49042         if (wasCanvasTouched) {
49043             e.preventDefault();
49044             // var touch = event.changedTouches[0];
49045             // _this._strokeEnd(touch);
49046             this.strokeEnd(e);
49047         }
49048     },
49049     
49050     reset: function () {
49051         this._lastPoints = [];
49052         this._lastVelocity = 0;
49053         this._lastWidth = (this.min_width + this.max_width) / 2;
49054         this.canvasElCtx().fillStyle = this.dot_color;
49055     },
49056     
49057     strokeMoveUpdate: function(e)
49058     {
49059         this.strokeUpdate(e);
49060         
49061         if (this.throttle) {
49062             this.throttleStroke(this.strokeUpdate, this.throttle);
49063         }
49064         else {
49065             this.strokeUpdate(e);
49066         }
49067     },
49068     
49069     strokeBegin: function(e)
49070     {
49071         var newPointGroup = {
49072             color: this.dot_color,
49073             points: []
49074         };
49075         
49076         if (typeof this.onBegin === 'function') {
49077             this.onBegin(e);
49078         }
49079         
49080         this.curve_data.push(newPointGroup);
49081         this.reset();
49082         this.strokeUpdate(e);
49083     },
49084     
49085     strokeUpdate: function(e)
49086     {
49087         var rect = this.canvasEl().dom.getBoundingClientRect();
49088         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49089         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49090         var lastPoints = lastPointGroup.points;
49091         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49092         var isLastPointTooClose = lastPoint
49093             ? point.distanceTo(lastPoint) <= this.min_distance
49094             : false;
49095         var color = lastPointGroup.color;
49096         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49097             var curve = this.addPoint(point);
49098             if (!lastPoint) {
49099                 this.drawDot({color: color, point: point});
49100             }
49101             else if (curve) {
49102                 this.drawCurve({color: color, curve: curve});
49103             }
49104             lastPoints.push({
49105                 time: point.time,
49106                 x: point.x,
49107                 y: point.y
49108             });
49109         }
49110     },
49111     
49112     strokeEnd: function(e)
49113     {
49114         this.strokeUpdate(e);
49115         if (typeof this.onEnd === 'function') {
49116             this.onEnd(e);
49117         }
49118     },
49119     
49120     addPoint:  function (point) {
49121         var _lastPoints = this._lastPoints;
49122         _lastPoints.push(point);
49123         if (_lastPoints.length > 2) {
49124             if (_lastPoints.length === 3) {
49125                 _lastPoints.unshift(_lastPoints[0]);
49126             }
49127             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49128             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49129             _lastPoints.shift();
49130             return curve;
49131         }
49132         return null;
49133     },
49134     
49135     calculateCurveWidths: function (startPoint, endPoint) {
49136         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49137             (1 - this.velocity_filter_weight) * this._lastVelocity;
49138
49139         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49140         var widths = {
49141             end: newWidth,
49142             start: this._lastWidth
49143         };
49144         
49145         this._lastVelocity = velocity;
49146         this._lastWidth = newWidth;
49147         return widths;
49148     },
49149     
49150     drawDot: function (_a) {
49151         var color = _a.color, point = _a.point;
49152         var ctx = this.canvasElCtx();
49153         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49154         ctx.beginPath();
49155         this.drawCurveSegment(point.x, point.y, width);
49156         ctx.closePath();
49157         ctx.fillStyle = color;
49158         ctx.fill();
49159     },
49160     
49161     drawCurve: function (_a) {
49162         var color = _a.color, curve = _a.curve;
49163         var ctx = this.canvasElCtx();
49164         var widthDelta = curve.endWidth - curve.startWidth;
49165         var drawSteps = Math.floor(curve.length()) * 2;
49166         ctx.beginPath();
49167         ctx.fillStyle = color;
49168         for (var i = 0; i < drawSteps; i += 1) {
49169         var t = i / drawSteps;
49170         var tt = t * t;
49171         var ttt = tt * t;
49172         var u = 1 - t;
49173         var uu = u * u;
49174         var uuu = uu * u;
49175         var x = uuu * curve.startPoint.x;
49176         x += 3 * uu * t * curve.control1.x;
49177         x += 3 * u * tt * curve.control2.x;
49178         x += ttt * curve.endPoint.x;
49179         var y = uuu * curve.startPoint.y;
49180         y += 3 * uu * t * curve.control1.y;
49181         y += 3 * u * tt * curve.control2.y;
49182         y += ttt * curve.endPoint.y;
49183         var width = curve.startWidth + ttt * widthDelta;
49184         this.drawCurveSegment(x, y, width);
49185         }
49186         ctx.closePath();
49187         ctx.fill();
49188     },
49189     
49190     drawCurveSegment: function (x, y, width) {
49191         var ctx = this.canvasElCtx();
49192         ctx.moveTo(x, y);
49193         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49194         this.is_empty = false;
49195     },
49196     
49197     clear: function()
49198     {
49199         var ctx = this.canvasElCtx();
49200         var canvas = this.canvasEl().dom;
49201         ctx.fillStyle = this.bg_color;
49202         ctx.clearRect(0, 0, canvas.width, canvas.height);
49203         ctx.fillRect(0, 0, canvas.width, canvas.height);
49204         this.curve_data = [];
49205         this.reset();
49206         this.is_empty = true;
49207     },
49208     
49209     fileEl: function()
49210     {
49211         return  this.el.select('input',true).first();
49212     },
49213     
49214     canvasEl: function()
49215     {
49216         return this.el.select('canvas',true).first();
49217     },
49218     
49219     canvasElCtx: function()
49220     {
49221         return this.el.select('canvas',true).first().dom.getContext('2d');
49222     },
49223     
49224     getImage: function(type)
49225     {
49226         if(this.is_empty) {
49227             return false;
49228         }
49229         
49230         // encryption ?
49231         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49232     },
49233     
49234     drawFromImage: function(img_src)
49235     {
49236         var img = new Image();
49237         
49238         img.onload = function(){
49239             this.canvasElCtx().drawImage(img, 0, 0);
49240         }.bind(this);
49241         
49242         img.src = img_src;
49243         
49244         this.is_empty = false;
49245     },
49246     
49247     selectImage: function()
49248     {
49249         this.fileEl().dom.click();
49250     },
49251     
49252     uploadImage: function(e)
49253     {
49254         var reader = new FileReader();
49255         
49256         reader.onload = function(e){
49257             var img = new Image();
49258             img.onload = function(){
49259                 this.reset();
49260                 this.canvasElCtx().drawImage(img, 0, 0);
49261             }.bind(this);
49262             img.src = e.target.result;
49263         }.bind(this);
49264         
49265         reader.readAsDataURL(e.target.files[0]);
49266     },
49267     
49268     // Bezier Point Constructor
49269     Point: (function () {
49270         function Point(x, y, time) {
49271             this.x = x;
49272             this.y = y;
49273             this.time = time || Date.now();
49274         }
49275         Point.prototype.distanceTo = function (start) {
49276             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49277         };
49278         Point.prototype.equals = function (other) {
49279             return this.x === other.x && this.y === other.y && this.time === other.time;
49280         };
49281         Point.prototype.velocityFrom = function (start) {
49282             return this.time !== start.time
49283             ? this.distanceTo(start) / (this.time - start.time)
49284             : 0;
49285         };
49286         return Point;
49287     }()),
49288     
49289     
49290     // Bezier Constructor
49291     Bezier: (function () {
49292         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49293             this.startPoint = startPoint;
49294             this.control2 = control2;
49295             this.control1 = control1;
49296             this.endPoint = endPoint;
49297             this.startWidth = startWidth;
49298             this.endWidth = endWidth;
49299         }
49300         Bezier.fromPoints = function (points, widths, scope) {
49301             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49302             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49303             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49304         };
49305         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49306             var dx1 = s1.x - s2.x;
49307             var dy1 = s1.y - s2.y;
49308             var dx2 = s2.x - s3.x;
49309             var dy2 = s2.y - s3.y;
49310             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49311             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49312             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49313             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49314             var dxm = m1.x - m2.x;
49315             var dym = m1.y - m2.y;
49316             var k = l2 / (l1 + l2);
49317             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49318             var tx = s2.x - cm.x;
49319             var ty = s2.y - cm.y;
49320             return {
49321                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49322                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49323             };
49324         };
49325         Bezier.prototype.length = function () {
49326             var steps = 10;
49327             var length = 0;
49328             var px;
49329             var py;
49330             for (var i = 0; i <= steps; i += 1) {
49331                 var t = i / steps;
49332                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49333                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49334                 if (i > 0) {
49335                     var xdiff = cx - px;
49336                     var ydiff = cy - py;
49337                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49338                 }
49339                 px = cx;
49340                 py = cy;
49341             }
49342             return length;
49343         };
49344         Bezier.prototype.point = function (t, start, c1, c2, end) {
49345             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49346             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49347             + (3.0 * c2 * (1.0 - t) * t * t)
49348             + (end * t * t * t);
49349         };
49350         return Bezier;
49351     }()),
49352     
49353     throttleStroke: function(fn, wait) {
49354       if (wait === void 0) { wait = 250; }
49355       var previous = 0;
49356       var timeout = null;
49357       var result;
49358       var storedContext;
49359       var storedArgs;
49360       var later = function () {
49361           previous = Date.now();
49362           timeout = null;
49363           result = fn.apply(storedContext, storedArgs);
49364           if (!timeout) {
49365               storedContext = null;
49366               storedArgs = [];
49367           }
49368       };
49369       return function wrapper() {
49370           var args = [];
49371           for (var _i = 0; _i < arguments.length; _i++) {
49372               args[_i] = arguments[_i];
49373           }
49374           var now = Date.now();
49375           var remaining = wait - (now - previous);
49376           storedContext = this;
49377           storedArgs = args;
49378           if (remaining <= 0 || remaining > wait) {
49379               if (timeout) {
49380                   clearTimeout(timeout);
49381                   timeout = null;
49382               }
49383               previous = now;
49384               result = fn.apply(storedContext, storedArgs);
49385               if (!timeout) {
49386                   storedContext = null;
49387                   storedArgs = [];
49388               }
49389           }
49390           else if (!timeout) {
49391               timeout = window.setTimeout(later, remaining);
49392           }
49393           return result;
49394       };
49395   }
49396   
49397 });
49398
49399  
49400
49401  // old names for form elements
49402 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49403 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49404 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49405 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49406 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49407 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49408 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49409 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49410 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49411 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49412 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49413 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49414 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49415 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49416 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49417 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49418 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49419 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49420 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49421 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49422 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49423 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49424 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49425 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49426 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49427 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49428
49429 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49430 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49431
49432 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49433 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49434
49435 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49436 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49437 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49438 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49439