Roo/bootstrap/Component.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
21 Roo.htmleditor = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
23 /*
24  * Based on:
25  * Ext JS Library 1.1.1
26  * Copyright(c) 2006-2007, Ext JS, LLC.
27  *
28  * Originally Released Under LGPL - original licence link has changed is not relivant.
29  *
30  * Fork - LGPL
31  * <script type="text/javascript">
32  */
33
34
35 /**
36  * @class Roo.Shadow
37  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
38  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
39  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
40  * @constructor
41  * Create a new Shadow
42  * @param {Object} config The config object
43  */
44 Roo.Shadow = function(config){
45     Roo.apply(this, config);
46     if(typeof this.mode != "string"){
47         this.mode = this.defaultMode;
48     }
49     var o = this.offset, a = {h: 0};
50     var rad = Math.floor(this.offset/2);
51     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
52         case "drop":
53             a.w = 0;
54             a.l = a.t = o;
55             a.t -= 1;
56             if(Roo.isIE){
57                 a.l -= this.offset + rad;
58                 a.t -= this.offset + rad;
59                 a.w -= rad;
60                 a.h -= rad;
61                 a.t += 1;
62             }
63         break;
64         case "sides":
65             a.w = (o*2);
66             a.l = -o;
67             a.t = o-1;
68             if(Roo.isIE){
69                 a.l -= (this.offset - rad);
70                 a.t -= this.offset + rad;
71                 a.l += 1;
72                 a.w -= (this.offset - rad)*2;
73                 a.w -= rad + 1;
74                 a.h -= 1;
75             }
76         break;
77         case "frame":
78             a.w = a.h = (o*2);
79             a.l = a.t = -o;
80             a.t += 1;
81             a.h -= 2;
82             if(Roo.isIE){
83                 a.l -= (this.offset - rad);
84                 a.t -= (this.offset - rad);
85                 a.l += 1;
86                 a.w -= (this.offset + rad + 1);
87                 a.h -= (this.offset + rad);
88                 a.h += 1;
89             }
90         break;
91     };
92
93     this.adjusts = a;
94 };
95
96 Roo.Shadow.prototype = {
97     /**
98      * @cfg {String} mode
99      * The shadow display mode.  Supports the following options:<br />
100      * sides: Shadow displays on both sides and bottom only<br />
101      * frame: Shadow displays equally on all four sides<br />
102      * drop: Traditional bottom-right drop shadow (default)
103      */
104     mode: false,
105     /**
106      * @cfg {String} offset
107      * The number of pixels to offset the shadow from the element (defaults to 4)
108      */
109     offset: 4,
110
111     // private
112     defaultMode: "drop",
113
114     /**
115      * Displays the shadow under the target element
116      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
117      */
118     show : function(target){
119         target = Roo.get(target);
120         if(!this.el){
121             this.el = Roo.Shadow.Pool.pull();
122             if(this.el.dom.nextSibling != target.dom){
123                 this.el.insertBefore(target);
124             }
125         }
126         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
127         if(Roo.isIE){
128             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
129         }
130         this.realign(
131             target.getLeft(true),
132             target.getTop(true),
133             target.getWidth(),
134             target.getHeight()
135         );
136         this.el.dom.style.display = "block";
137     },
138
139     /**
140      * Returns true if the shadow is visible, else false
141      */
142     isVisible : function(){
143         return this.el ? true : false;  
144     },
145
146     /**
147      * Direct alignment when values are already available. Show must be called at least once before
148      * calling this method to ensure it is initialized.
149      * @param {Number} left The target element left position
150      * @param {Number} top The target element top position
151      * @param {Number} width The target element width
152      * @param {Number} height The target element height
153      */
154     realign : function(l, t, w, h){
155         if(!this.el){
156             return;
157         }
158         var a = this.adjusts, d = this.el.dom, s = d.style;
159         var iea = 0;
160         s.left = (l+a.l)+"px";
161         s.top = (t+a.t)+"px";
162         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
163  
164         if(s.width != sws || s.height != shs){
165             s.width = sws;
166             s.height = shs;
167             if(!Roo.isIE){
168                 var cn = d.childNodes;
169                 var sww = Math.max(0, (sw-12))+"px";
170                 cn[0].childNodes[1].style.width = sww;
171                 cn[1].childNodes[1].style.width = sww;
172                 cn[2].childNodes[1].style.width = sww;
173                 cn[1].style.height = Math.max(0, (sh-12))+"px";
174             }
175         }
176     },
177
178     /**
179      * Hides this shadow
180      */
181     hide : function(){
182         if(this.el){
183             this.el.dom.style.display = "none";
184             Roo.Shadow.Pool.push(this.el);
185             delete this.el;
186         }
187     },
188
189     /**
190      * Adjust the z-index of this shadow
191      * @param {Number} zindex The new z-index
192      */
193     setZIndex : function(z){
194         this.zIndex = z;
195         if(this.el){
196             this.el.setStyle("z-index", z);
197         }
198     }
199 };
200
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
203     var p = [];
204     var markup = Roo.isIE ?
205                  '<div class="x-ie-shadow"></div>' :
206                  '<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>';
207     return {
208         pull : function(){
209             var sh = p.shift();
210             if(!sh){
211                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212                 sh.autoBoxAdjust = false;
213             }
214             return sh;
215         },
216
217         push : function(sh){
218             p.push(sh);
219         }
220     };
221 }();/*
222  * - LGPL
223  *
224  * base class for bootstrap elements.
225  * 
226  */
227
228 Roo.bootstrap = Roo.bootstrap || {};
229 /**
230  * @class Roo.bootstrap.Component
231  * @extends Roo.Component
232  * @abstract
233  * @children Roo.bootstrap.Component
234  * Bootstrap Component base class
235  * @cfg {String} cls css class
236  * @cfg {String} style any extra css
237  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
239  * @cfg {string} dataId cutomer id
240  * @cfg {string} name Specifies name attribute
241  * @cfg {string} tooltip  Text for the tooltip
242  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
243  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
244  
245  * @constructor
246  * Do not use directly - it does not do anything..
247  * @param {Object} config The config object
248  */
249
250
251
252 Roo.bootstrap.Component = function(config){
253     Roo.bootstrap.Component.superclass.constructor.call(this, config);
254        
255     this.addEvents({
256         /**
257          * @event childrenrendered
258          * Fires when the children have been rendered..
259          * @param {Roo.bootstrap.Component} this
260          */
261         "childrenrendered" : true
262         
263         
264         
265     });
266     
267     
268 };
269
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
271     
272     
273     allowDomMove : false, // to stop relocations in parent onRender...
274     
275     cls : false,
276     
277     style : false,
278     
279     autoCreate : false,
280     
281     tooltip : null,
282     /**
283      * Initialize Events for the element
284      */
285     initEvents : function() { },
286     
287     xattr : false,
288     
289     parentId : false,
290     
291     can_build_overlaid : true,
292     
293     container_method : false,
294     
295     dataId : false,
296     
297     name : false,
298     
299     parent: function() {
300         // returns the parent component..
301         return Roo.ComponentMgr.get(this.parentId)
302         
303         
304     },
305     
306     // private
307     onRender : function(ct, position)
308     {
309        // Roo.log("Call onRender: " + this.xtype);
310         
311         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312         
313         if(this.el){
314             if (this.el.attr('xtype')) {
315                 this.el.attr('xtypex', this.el.attr('xtype'));
316                 this.el.dom.removeAttribute('xtype');
317                 
318                 this.initEvents();
319             }
320             
321             return;
322         }
323         
324          
325         
326         var cfg = Roo.apply({},  this.getAutoCreate());
327         
328         cfg.id = this.id || Roo.id();
329         
330         // fill in the extra attributes 
331         if (this.xattr && typeof(this.xattr) =='object') {
332             for (var i in this.xattr) {
333                 cfg[i] = this.xattr[i];
334             }
335         }
336         
337         if(this.dataId){
338             cfg.dataId = this.dataId;
339         }
340         
341         if (this.cls) {
342             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343         }
344         
345         if (this.style) { // fixme needs to support more complex style data.
346             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347         }
348         
349         if(this.name){
350             cfg.name = this.name;
351         }
352         
353         this.el = ct.createChild(cfg, position);
354         
355         if (this.tooltip) {
356             this.tooltipEl().attr('tooltip', this.tooltip);
357         }
358         
359         if(this.tabIndex !== undefined){
360             this.el.dom.setAttribute('tabIndex', this.tabIndex);
361         }
362         
363         this.initEvents();
364         
365     },
366     /**
367      * Fetch the element to add children to
368      * @return {Roo.Element} defaults to this.el
369      */
370     getChildContainer : function()
371     {
372         return this.el;
373     },
374     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
375     {
376         return Roo.get(document.body);
377     },
378     
379     /**
380      * Fetch the element to display the tooltip on.
381      * @return {Roo.Element} defaults to this.el
382      */
383     tooltipEl : function()
384     {
385         return this.el;
386     },
387         
388     addxtype  : function(tree,cntr)
389     {
390         var cn = this;
391         
392         cn = Roo.factory(tree);
393         //Roo.log(['addxtype', cn]);
394            
395         cn.parentType = this.xtype; //??
396         cn.parentId = this.id;
397         
398         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
399         if (typeof(cn.container_method) == 'string') {
400             cntr = cn.container_method;
401         }
402         
403         
404         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
405         
406         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
407         
408         var build_from_html =  Roo.XComponent.build_from_html;
409           
410         var is_body  = (tree.xtype == 'Body') ;
411           
412         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
413           
414         var self_cntr_el = Roo.get(this[cntr](false));
415         
416         // do not try and build conditional elements 
417         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418             return false;
419         }
420         
421         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
422             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
423                 return this.addxtypeChild(tree,cntr, is_body);
424             }
425             
426             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
427                 
428             if(echild){
429                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
430             }
431             
432             Roo.log('skipping render');
433             return cn;
434             
435         }
436         
437         var ret = false;
438         if (!build_from_html) {
439             return false;
440         }
441         
442         // this i think handles overlaying multiple children of the same type
443         // with the sam eelement.. - which might be buggy..
444         while (true) {
445             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
446             
447             if (!echild) {
448                 break;
449             }
450             
451             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452                 break;
453             }
454             
455             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456         }
457        
458         return ret;
459     },
460     
461     
462     addxtypeChild : function (tree, cntr, is_body)
463     {
464         Roo.debug && Roo.log('addxtypeChild:' + cntr);
465         Roo.log('ADDXTYPECHILD');
466         Roo.log(tree);
467         var cn = this;
468         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
469         
470         
471         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
472                     (typeof(tree['flexy:foreach']) != 'undefined');
473           
474     
475         
476         skip_children = false;
477         // render the element if it's not BODY.
478         if (!is_body) {
479             
480             // if parent was disabled, then do not try and create the children..
481             if(!this[cntr](true)){
482                 tree.items = [];
483                 return tree;
484             }
485            
486             cn = Roo.factory(tree);
487
488             Roo.log(cn);
489            
490             cn.parentType = this.xtype; //??
491             cn.parentId = this.id;
492             
493             var build_from_html =  Roo.XComponent.build_from_html;
494             
495             
496             // does the container contain child eleemnts with 'xtype' attributes.
497             // that match this xtype..
498             // note - when we render we create these as well..
499             // so we should check to see if body has xtype set.
500             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
501                
502                 var self_cntr_el = Roo.get(this[cntr](false));
503                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
504                 if (echild) { 
505                     //Roo.log(Roo.XComponent.build_from_html);
506                     //Roo.log("got echild:");
507                     //Roo.log(echild);
508                 }
509                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
510                 // and are not displayed -this causes this to use up the wrong element when matching.
511                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
512                 
513                 
514                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
515                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
516                   
517                   
518                   
519                     cn.el = echild;
520                   //  Roo.log("GOT");
521                     //echild.dom.removeAttribute('xtype');
522                 } else {
523                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
524                     Roo.debug && Roo.log(self_cntr_el);
525                     Roo.debug && Roo.log(echild);
526                     Roo.debug && Roo.log(cn);
527                 }
528             }
529            
530             
531            
532             // if object has flexy:if - then it may or may not be rendered.
533             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
534                 // skip a flexy if element.
535                 Roo.debug && Roo.log('skipping render');
536                 Roo.debug && Roo.log(tree);
537                 if (!cn.el) {
538                     Roo.debug && Roo.log('skipping all children');
539                     skip_children = true;
540                 }
541                 
542              } else {
543                  
544                 // actually if flexy:foreach is found, we really want to create 
545                 // multiple copies here...
546                 //Roo.log('render');
547                 //Roo.log(this[cntr]());
548                 // some elements do not have render methods.. like the layouts...
549                 /*
550                 if(this[cntr](true) === false){
551                     cn.items = [];
552                     return cn;
553                 }
554                 */
555                 cn.render && cn.render(this[cntr](true));
556                 
557              }
558             // then add the element..
559         }
560          
561         // handle the kids..
562         
563         var nitems = [];
564         /*
565         if (typeof (tree.menu) != 'undefined') {
566             tree.menu.parentType = cn.xtype;
567             tree.menu.triggerEl = cn.el;
568             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
569             
570         }
571         */
572         if (!tree.items || !tree.items.length) {
573             cn.items = nitems;
574             //Roo.log(["no children", this]);
575             
576             return cn;
577         }
578          
579         var items = tree.items;
580         delete tree.items;
581         
582         //Roo.log(items.length);
583             // add the items..
584         if (!skip_children) {    
585             for(var i =0;i < items.length;i++) {
586               //  Roo.log(['add child', items[i]]);
587                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
588             }
589         }
590         
591         cn.items = nitems;
592         
593         //Roo.log("fire childrenrendered");
594         
595         cn.fireEvent('childrenrendered', this);
596         
597         return cn;
598     },
599     
600     /**
601      * Set the element that will be used to show or hide
602      */
603     setVisibilityEl : function(el)
604     {
605         this.visibilityEl = el;
606     },
607     
608      /**
609      * Get the element that will be used to show or hide
610      */
611     getVisibilityEl : function()
612     {
613         if (typeof(this.visibilityEl) == 'object') {
614             return this.visibilityEl;
615         }
616         
617         if (typeof(this.visibilityEl) == 'string') {
618             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
619         }
620         
621         return this.getEl();
622     },
623     
624     /**
625      * Show a component - removes 'hidden' class
626      */
627     show : function()
628     {
629         if(!this.getVisibilityEl()){
630             return;
631         }
632          
633         this.getVisibilityEl().removeClass(['hidden','d-none']);
634         
635         this.fireEvent('show', this);
636         
637         
638     },
639     /**
640      * Hide a component - adds 'hidden' class
641      */
642     hide: function()
643     {
644         if(!this.getVisibilityEl()){
645             return;
646         }
647         
648         this.getVisibilityEl().addClass(['hidden','d-none']);
649         
650         this.fireEvent('hide', this);
651         
652     }
653 });
654
655  /*
656  * - LGPL
657  *
658  * element
659  * 
660  */
661
662 /**
663  * @class Roo.bootstrap.Element
664  * @extends Roo.bootstrap.Component
665  * @children Roo.bootstrap.Component
666  * Bootstrap Element class (basically a DIV used to make random stuff )
667  * 
668  * @cfg {String} html contents of the element
669  * @cfg {String} tag tag of the element
670  * @cfg {String} cls class of the element
671  * @cfg {Boolean} preventDefault (true|false) default false
672  * @cfg {Boolean} clickable (true|false) default false
673  * @cfg {String} role default blank - set to button to force cursor pointer
674  
675  * 
676  * @constructor
677  * Create a new Element
678  * @param {Object} config The config object
679  */
680
681 Roo.bootstrap.Element = function(config){
682     Roo.bootstrap.Element.superclass.constructor.call(this, config);
683     
684     this.addEvents({
685         // raw events
686         /**
687          * @event click
688          * When a element is chick
689          * @param {Roo.bootstrap.Element} this
690          * @param {Roo.EventObject} e
691          */
692         "click" : true 
693         
694       
695     });
696 };
697
698 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
699     
700     tag: 'div',
701     cls: '',
702     html: '',
703     preventDefault: false, 
704     clickable: false,
705     tapedTwice : false,
706     role : false,
707     
708     getAutoCreate : function(){
709         
710         var cfg = {
711             tag: this.tag,
712             // cls: this.cls, double assign in parent class Component.js :: onRender
713             html: this.html
714         };
715         if (this.role !== false) {
716             cfg.role = this.role;
717         }
718         
719         return cfg;
720     },
721     
722     initEvents: function() 
723     {
724         Roo.bootstrap.Element.superclass.initEvents.call(this);
725         
726         if(this.clickable){
727             this.el.on('click', this.onClick, this);
728         }
729         
730         
731     },
732     
733     onClick : function(e)
734     {
735         if(this.preventDefault){
736             e.preventDefault();
737         }
738         
739         this.fireEvent('click', this, e); // why was this double click before?
740     },
741     
742     
743     
744
745     
746     
747     getValue : function()
748     {
749         return this.el.dom.innerHTML;
750     },
751     
752     setValue : function(value)
753     {
754         this.el.dom.innerHTML = value;
755     }
756    
757 });
758
759  
760
761  /*
762  * - LGPL
763  *
764  * dropable area
765  * 
766  */
767
768 /**
769  * @class Roo.bootstrap.DropTarget
770  * @extends Roo.bootstrap.Element
771  * Bootstrap DropTarget class
772  
773  * @cfg {string} name dropable name
774  * 
775  * @constructor
776  * Create a new Dropable Area
777  * @param {Object} config The config object
778  */
779
780 Roo.bootstrap.DropTarget = function(config){
781     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
782     
783     this.addEvents({
784         // raw events
785         /**
786          * @event click
787          * When a element is chick
788          * @param {Roo.bootstrap.Element} this
789          * @param {Roo.EventObject} e
790          */
791         "drop" : true
792     });
793 };
794
795 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
796     
797     
798     getAutoCreate : function(){
799         
800          
801     },
802     
803     initEvents: function() 
804     {
805         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
806         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
807             ddGroup: this.name,
808             listeners : {
809                 drop : this.dragDrop.createDelegate(this),
810                 enter : this.dragEnter.createDelegate(this),
811                 out : this.dragOut.createDelegate(this),
812                 over : this.dragOver.createDelegate(this)
813             }
814             
815         });
816         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
817     },
818     
819     dragDrop : function(source,e,data)
820     {
821         // user has to decide how to impliment this.
822         Roo.log('drop');
823         Roo.log(this);
824         //this.fireEvent('drop', this, source, e ,data);
825         return false;
826     },
827     
828     dragEnter : function(n, dd, e, data)
829     {
830         // probably want to resize the element to match the dropped element..
831         Roo.log("enter");
832         this.originalSize = this.el.getSize();
833         this.el.setSize( n.el.getSize());
834         this.dropZone.DDM.refreshCache(this.name);
835         Roo.log([n, dd, e, data]);
836     },
837     
838     dragOut : function(value)
839     {
840         // resize back to normal
841         Roo.log("out");
842         this.el.setSize(this.originalSize);
843         this.dropZone.resetConstraints();
844     },
845     
846     dragOver : function()
847     {
848         // ??? do nothing?
849     }
850    
851 });
852
853  
854
855  /*
856  * - LGPL
857  *
858  * Body
859  *
860  */
861
862 /**
863  * @class Roo.bootstrap.Body
864  * @extends Roo.bootstrap.Component
865  * @children Roo.bootstrap.Component 
866  * @parent none builder
867  * Bootstrap Body class
868  *
869  * @constructor
870  * Create a new body
871  * @param {Object} config The config object
872  */
873
874 Roo.bootstrap.Body = function(config){
875
876     config = config || {};
877
878     Roo.bootstrap.Body.superclass.constructor.call(this, config);
879     this.el = Roo.get(config.el ? config.el : document.body );
880     if (this.cls && this.cls.length) {
881         Roo.get(document.body).addClass(this.cls);
882     }
883 };
884
885 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
886
887     is_body : true,// just to make sure it's constructed?
888
889         autoCreate : {
890         cls: 'container'
891     },
892     onRender : function(ct, position)
893     {
894        /* Roo.log("Roo.bootstrap.Body - onRender");
895         if (this.cls && this.cls.length) {
896             Roo.get(document.body).addClass(this.cls);
897         }
898         // style??? xttr???
899         */
900     }
901
902
903
904
905 });
906 /*
907  * - LGPL
908  *
909  * button group
910  * 
911  */
912
913
914 /**
915  * @class Roo.bootstrap.ButtonGroup
916  * @extends Roo.bootstrap.Component
917  * Bootstrap ButtonGroup class
918  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
919  * 
920  * @cfg {String} size lg | sm | xs (default empty normal)
921  * @cfg {String} align vertical | justified  (default none)
922  * @cfg {String} direction up | down (default down)
923  * @cfg {Boolean} toolbar false | true
924  * @cfg {Boolean} btn true | false
925  * 
926  * 
927  * @constructor
928  * Create a new Input
929  * @param {Object} config The config object
930  */
931
932 Roo.bootstrap.ButtonGroup = function(config){
933     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
934 };
935
936 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
937     
938     size: '',
939     align: '',
940     direction: '',
941     toolbar: false,
942     btn: true,
943
944     getAutoCreate : function(){
945         var cfg = {
946             cls: 'btn-group',
947             html : null
948         };
949         
950         cfg.html = this.html || cfg.html;
951         
952         if (this.toolbar) {
953             cfg = {
954                 cls: 'btn-toolbar',
955                 html: null
956             };
957             
958             return cfg;
959         }
960         
961         if (['vertical','justified'].indexOf(this.align)!==-1) {
962             cfg.cls = 'btn-group-' + this.align;
963             
964             if (this.align == 'justified') {
965                 console.log(this.items);
966             }
967         }
968         
969         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
970             cfg.cls += ' btn-group-' + this.size;
971         }
972         
973         if (this.direction == 'up') {
974             cfg.cls += ' dropup' ;
975         }
976         
977         return cfg;
978     },
979     /**
980      * Add a button to the group (similar to NavItem API.)
981      */
982     addItem : function(cfg)
983     {
984         var cn = new Roo.bootstrap.Button(cfg);
985         //this.register(cn);
986         cn.parentId = this.id;
987         cn.onRender(this.el, null);
988         return cn;
989     }
990    
991 });
992
993  /*
994  * - LGPL
995  *
996  * button
997  * 
998  */
999
1000 /**
1001  * @class Roo.bootstrap.Button
1002  * @extends Roo.bootstrap.Component
1003  * Bootstrap Button class
1004  * @cfg {String} html The button content
1005  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1006  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1007  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1008  * @cfg {String} size (lg|sm|xs)
1009  * @cfg {String} tag (a|input|submit)
1010  * @cfg {String} href empty or href
1011  * @cfg {Boolean} disabled default false;
1012  * @cfg {Boolean} isClose default false;
1013  * @cfg {String} glyphicon depricated - use fa
1014  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1015  * @cfg {String} badge text for badge
1016  * @cfg {String} theme (default|glow)  
1017  * @cfg {Boolean} inverse dark themed version
1018  * @cfg {Boolean} toggle is it a slidy toggle button
1019  * @cfg {Boolean} pressed   default null - if the button ahs active state
1020  * @cfg {String} ontext text for on slidy toggle state
1021  * @cfg {String} offtext text for off slidy toggle state
1022  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1023  * @cfg {Boolean} removeClass remove the standard class..
1024  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1025  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1026  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1027
1028  * @constructor
1029  * Create a new button
1030  * @param {Object} config The config object
1031  */
1032
1033
1034 Roo.bootstrap.Button = function(config){
1035     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1036     
1037     this.addEvents({
1038         // raw events
1039         /**
1040          * @event click
1041          * When a button is pressed
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "click" : true,
1046         /**
1047          * @event dblclick
1048          * When a button is double clicked
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          */
1052         "dblclick" : true,
1053          /**
1054          * @event toggle
1055          * After the button has been toggles
1056          * @param {Roo.bootstrap.Button} btn
1057          * @param {Roo.EventObject} e
1058          * @param {boolean} pressed (also available as button.pressed)
1059          */
1060         "toggle" : true
1061     });
1062 };
1063
1064 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1065     html: false,
1066     active: false,
1067     weight: '',
1068     badge_weight: '',
1069     outline : false,
1070     size: '',
1071     tag: 'button',
1072     href: '',
1073     disabled: false,
1074     isClose: false,
1075     glyphicon: '',
1076     fa: '',
1077     badge: '',
1078     theme: 'default',
1079     inverse: false,
1080     
1081     toggle: false,
1082     ontext: 'ON',
1083     offtext: 'OFF',
1084     defaulton: true,
1085     preventDefault: true,
1086     removeClass: false,
1087     name: false,
1088     target: false,
1089     group : false,
1090      
1091     pressed : null,
1092      
1093     
1094     getAutoCreate : function(){
1095         
1096         var cfg = {
1097             tag : 'button',
1098             cls : 'roo-button',
1099             html: ''
1100         };
1101         
1102         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1103             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1104             this.tag = 'button';
1105         } else {
1106             cfg.tag = this.tag;
1107         }
1108         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1109         
1110         if (this.toggle == true) {
1111             cfg={
1112                 tag: 'div',
1113                 cls: 'slider-frame roo-button',
1114                 cn: [
1115                     {
1116                         tag: 'span',
1117                         'data-on-text':'ON',
1118                         'data-off-text':'OFF',
1119                         cls: 'slider-button',
1120                         html: this.offtext
1121                     }
1122                 ]
1123             };
1124             // why are we validating the weights?
1125             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126                 cfg.cls +=  ' ' + this.weight;
1127             }
1128             
1129             return cfg;
1130         }
1131         
1132         if (this.isClose) {
1133             cfg.cls += ' close';
1134             
1135             cfg["aria-hidden"] = true;
1136             
1137             cfg.html = "&times;";
1138             
1139             return cfg;
1140         }
1141              
1142         
1143         if (this.theme==='default') {
1144             cfg.cls = 'btn roo-button';
1145             
1146             //if (this.parentType != 'Navbar') {
1147             this.weight = this.weight.length ?  this.weight : 'default';
1148             //}
1149             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1150                 
1151                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1152                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1153                 cfg.cls += ' btn-' + outline + weight;
1154                 if (this.weight == 'default') {
1155                     // BC
1156                     cfg.cls += ' btn-' + this.weight;
1157                 }
1158             }
1159         } else if (this.theme==='glow') {
1160             
1161             cfg.tag = 'a';
1162             cfg.cls = 'btn-glow roo-button';
1163             
1164             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1165                 
1166                 cfg.cls += ' ' + this.weight;
1167             }
1168         }
1169    
1170         
1171         if (this.inverse) {
1172             this.cls += ' inverse';
1173         }
1174         
1175         
1176         if (this.active || this.pressed === true) {
1177             cfg.cls += ' active';
1178         }
1179         
1180         if (this.disabled) {
1181             cfg.disabled = 'disabled';
1182         }
1183         
1184         if (this.items) {
1185             Roo.log('changing to ul' );
1186             cfg.tag = 'ul';
1187             this.glyphicon = 'caret';
1188             if (Roo.bootstrap.version == 4) {
1189                 this.fa = 'caret-down';
1190             }
1191             
1192         }
1193         
1194         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1195          
1196         //gsRoo.log(this.parentType);
1197         if (this.parentType === 'Navbar' && !this.parent().bar) {
1198             Roo.log('changing to li?');
1199             
1200             cfg.tag = 'li';
1201             
1202             cfg.cls = '';
1203             cfg.cn =  [{
1204                 tag : 'a',
1205                 cls : 'roo-button',
1206                 html : this.html,
1207                 href : this.href || '#'
1208             }];
1209             if (this.menu) {
1210                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1211                 cfg.cls += ' dropdown';
1212             }   
1213             
1214             delete cfg.html;
1215             
1216         }
1217         
1218        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1219         
1220         if (this.glyphicon) {
1221             cfg.html = ' ' + cfg.html;
1222             
1223             cfg.cn = [
1224                 {
1225                     tag: 'span',
1226                     cls: 'glyphicon glyphicon-' + this.glyphicon
1227                 }
1228             ];
1229         }
1230         if (this.fa) {
1231             cfg.html = ' ' + cfg.html;
1232             
1233             cfg.cn = [
1234                 {
1235                     tag: 'i',
1236                     cls: 'fa fas fa-' + this.fa
1237                 }
1238             ];
1239         }
1240         
1241         if (this.badge) {
1242             cfg.html += ' ';
1243             
1244             cfg.tag = 'a';
1245             
1246 //            cfg.cls='btn roo-button';
1247             
1248             cfg.href=this.href;
1249             
1250             var value = cfg.html;
1251             
1252             if(this.glyphicon){
1253                 value = {
1254                     tag: 'span',
1255                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1256                     html: this.html
1257                 };
1258             }
1259             if(this.fa){
1260                 value = {
1261                     tag: 'i',
1262                     cls: 'fa fas fa-' + this.fa,
1263                     html: this.html
1264                 };
1265             }
1266             
1267             var bw = this.badge_weight.length ? this.badge_weight :
1268                 (this.weight.length ? this.weight : 'secondary');
1269             bw = bw == 'default' ? 'secondary' : bw;
1270             
1271             cfg.cn = [
1272                 value,
1273                 {
1274                     tag: 'span',
1275                     cls: 'badge badge-' + bw,
1276                     html: this.badge
1277                 }
1278             ];
1279             
1280             cfg.html='';
1281         }
1282         
1283         if (this.menu) {
1284             cfg.cls += ' dropdown';
1285             cfg.html = typeof(cfg.html) != 'undefined' ?
1286                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1287         }
1288         
1289         if (cfg.tag !== 'a' && this.href !== '') {
1290             throw "Tag must be a to set href.";
1291         } else if (this.href.length > 0) {
1292             cfg.href = this.href;
1293         }
1294         
1295         if(this.removeClass){
1296             cfg.cls = '';
1297         }
1298         
1299         if(this.target){
1300             cfg.target = this.target;
1301         }
1302         
1303         return cfg;
1304     },
1305     initEvents: function() {
1306        // Roo.log('init events?');
1307 //        Roo.log(this.el.dom);
1308         // add the menu...
1309         
1310         if (typeof (this.menu) != 'undefined') {
1311             this.menu.parentType = this.xtype;
1312             this.menu.triggerEl = this.el;
1313             this.addxtype(Roo.apply({}, this.menu));
1314         }
1315
1316
1317         if (this.el.hasClass('roo-button')) {
1318              this.el.on('click', this.onClick, this);
1319              this.el.on('dblclick', this.onDblClick, this);
1320         } else {
1321              this.el.select('.roo-button').on('click', this.onClick, this);
1322              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1323              
1324         }
1325         // why?
1326         if(this.removeClass){
1327             this.el.on('click', this.onClick, this);
1328         }
1329         
1330         if (this.group === true) {
1331              if (this.pressed === false || this.pressed === true) {
1332                 // nothing
1333             } else {
1334                 this.pressed = false;
1335                 this.setActive(this.pressed);
1336             }
1337             
1338         }
1339         
1340         this.el.enableDisplayMode();
1341         
1342     },
1343     onClick : function(e)
1344     {
1345         if (this.disabled) {
1346             return;
1347         }
1348         
1349         Roo.log('button on click ');
1350         if(this.href === '' || this.preventDefault){
1351             e.preventDefault();
1352         }
1353         
1354         if (this.group) {
1355             if (this.pressed) {
1356                 // do nothing -
1357                 return;
1358             }
1359             this.setActive(true);
1360             var pi = this.parent().items;
1361             for (var i = 0;i < pi.length;i++) {
1362                 if (this == pi[i]) {
1363                     continue;
1364                 }
1365                 if (pi[i].el.hasClass('roo-button')) {
1366                     pi[i].setActive(false);
1367                 }
1368             }
1369             this.fireEvent('click', this, e);            
1370             return;
1371         }
1372         
1373         if (this.pressed === true || this.pressed === false) {
1374             this.toggleActive(e);
1375         }
1376         
1377         
1378         this.fireEvent('click', this, e);
1379     },
1380     onDblClick: function(e)
1381     {
1382         if (this.disabled) {
1383             return;
1384         }
1385         if(this.preventDefault){
1386             e.preventDefault();
1387         }
1388         this.fireEvent('dblclick', this, e);
1389     },
1390     /**
1391      * Enables this button
1392      */
1393     enable : function()
1394     {
1395         this.disabled = false;
1396         this.el.removeClass('disabled');
1397         this.el.dom.removeAttribute("disabled");
1398     },
1399     
1400     /**
1401      * Disable this button
1402      */
1403     disable : function()
1404     {
1405         this.disabled = true;
1406         this.el.addClass('disabled');
1407         this.el.attr("disabled", "disabled")
1408     },
1409      /**
1410      * sets the active state on/off, 
1411      * @param {Boolean} state (optional) Force a particular state
1412      */
1413     setActive : function(v) {
1414         
1415         this.el[v ? 'addClass' : 'removeClass']('active');
1416         this.pressed = v;
1417     },
1418      /**
1419      * toggles the current active state 
1420      */
1421     toggleActive : function(e)
1422     {
1423         this.setActive(!this.pressed); // this modifies pressed...
1424         this.fireEvent('toggle', this, e, this.pressed);
1425     },
1426      /**
1427      * get the current active state
1428      * @return {boolean} true if it's active
1429      */
1430     isActive : function()
1431     {
1432         return this.el.hasClass('active');
1433     },
1434     /**
1435      * set the text of the first selected button
1436      */
1437     setText : function(str)
1438     {
1439         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1440     },
1441     /**
1442      * get the text of the first selected button
1443      */
1444     getText : function()
1445     {
1446         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1447     },
1448     
1449     setWeight : function(str)
1450     {
1451         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1452         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1453         this.weight = str;
1454         var outline = this.outline ? 'outline-' : '';
1455         if (str == 'default') {
1456             this.el.addClass('btn-default btn-outline-secondary');        
1457             return;
1458         }
1459         this.el.addClass('btn-' + outline + str);        
1460     }
1461     
1462     
1463 });
1464 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1465
1466 Roo.bootstrap.Button.weights = [
1467     'default',
1468     'secondary' ,
1469     'primary',
1470     'success',
1471     'info',
1472     'warning',
1473     'danger',
1474     'link',
1475     'light',
1476     'dark'              
1477    
1478 ];/*
1479  * - LGPL
1480  *
1481  * column
1482  * 
1483  */
1484
1485 /**
1486  * @class Roo.bootstrap.Column
1487  * @extends Roo.bootstrap.Component
1488  * @children Roo.bootstrap.Component
1489  * Bootstrap Column class
1490  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1491  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1492  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1493  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1494  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1495  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1496  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1497  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1498  *
1499  * 
1500  * @cfg {Boolean} hidden (true|false) hide the element
1501  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1502  * @cfg {String} fa (ban|check|...) font awesome icon
1503  * @cfg {Number} fasize (1|2|....) font awsome size
1504
1505  * @cfg {String} icon (info-sign|check|...) glyphicon name
1506
1507  * @cfg {String} html content of column.
1508  * 
1509  * @constructor
1510  * Create a new Column
1511  * @param {Object} config The config object
1512  */
1513
1514 Roo.bootstrap.Column = function(config){
1515     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1516 };
1517
1518 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1519     
1520     xs: false,
1521     sm: false,
1522     md: false,
1523     lg: false,
1524     xsoff: false,
1525     smoff: false,
1526     mdoff: false,
1527     lgoff: false,
1528     html: '',
1529     offset: 0,
1530     alert: false,
1531     fa: false,
1532     icon : false,
1533     hidden : false,
1534     fasize : 1,
1535     
1536     getAutoCreate : function(){
1537         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538         
1539         cfg = {
1540             tag: 'div',
1541             cls: 'column'
1542         };
1543         
1544         var settings=this;
1545         var sizes =   ['xs','sm','md','lg'];
1546         sizes.map(function(size ,ix){
1547             //Roo.log( size + ':' + settings[size]);
1548             
1549             if (settings[size+'off'] !== false) {
1550                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1551             }
1552             
1553             if (settings[size] === false) {
1554                 return;
1555             }
1556             
1557             if (!settings[size]) { // 0 = hidden
1558                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1559                 // bootsrap4
1560                 for (var i = ix; i > -1; i--) {
1561                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1562                 }
1563                 
1564                 
1565                 return;
1566             }
1567             cfg.cls += ' col-' + size + '-' + settings[size] + (
1568                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1569             );
1570             
1571         });
1572         
1573         if (this.hidden) {
1574             cfg.cls += ' hidden';
1575         }
1576         
1577         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1578             cfg.cls +=' alert alert-' + this.alert;
1579         }
1580         
1581         
1582         if (this.html.length) {
1583             cfg.html = this.html;
1584         }
1585         if (this.fa) {
1586             var fasize = '';
1587             if (this.fasize > 1) {
1588                 fasize = ' fa-' + this.fasize + 'x';
1589             }
1590             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1591             
1592             
1593         }
1594         if (this.icon) {
1595             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1596         }
1597         
1598         return cfg;
1599     }
1600    
1601 });
1602
1603  
1604
1605  /*
1606  * - LGPL
1607  *
1608  * page container.
1609  * 
1610  */
1611
1612
1613 /**
1614  * @class Roo.bootstrap.Container
1615  * @extends Roo.bootstrap.Component
1616  * @children Roo.bootstrap.Component
1617  * @parent builder
1618  * Bootstrap Container class
1619  * @cfg {Boolean} jumbotron is it a jumbotron element
1620  * @cfg {String} html content of element
1621  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1622  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1623  * @cfg {String} header content of header (for panel)
1624  * @cfg {String} footer content of footer (for panel)
1625  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1626  * @cfg {String} tag (header|aside|section) type of HTML tag.
1627  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1628  * @cfg {String} fa font awesome icon
1629  * @cfg {String} icon (info-sign|check|...) glyphicon name
1630  * @cfg {Boolean} hidden (true|false) hide the element
1631  * @cfg {Boolean} expandable (true|false) default false
1632  * @cfg {Boolean} expanded (true|false) default true
1633  * @cfg {String} rheader contet on the right of header
1634  * @cfg {Boolean} clickable (true|false) default false
1635
1636  *     
1637  * @constructor
1638  * Create a new Container
1639  * @param {Object} config The config object
1640  */
1641
1642 Roo.bootstrap.Container = function(config){
1643     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1644     
1645     this.addEvents({
1646         // raw events
1647          /**
1648          * @event expand
1649          * After the panel has been expand
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "expand" : true,
1654         /**
1655          * @event collapse
1656          * After the panel has been collapsed
1657          * 
1658          * @param {Roo.bootstrap.Container} this
1659          */
1660         "collapse" : true,
1661         /**
1662          * @event click
1663          * When a element is chick
1664          * @param {Roo.bootstrap.Container} this
1665          * @param {Roo.EventObject} e
1666          */
1667         "click" : true
1668     });
1669 };
1670
1671 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1672     
1673     jumbotron : false,
1674     well: '',
1675     panel : '',
1676     header: '',
1677     footer : '',
1678     sticky: '',
1679     tag : false,
1680     alert : false,
1681     fa: false,
1682     icon : false,
1683     expandable : false,
1684     rheader : '',
1685     expanded : true,
1686     clickable: false,
1687   
1688      
1689     getChildContainer : function() {
1690         
1691         if(!this.el){
1692             return false;
1693         }
1694         
1695         if (this.panel.length) {
1696             return this.el.select('.panel-body',true).first();
1697         }
1698         
1699         return this.el;
1700     },
1701     
1702     
1703     getAutoCreate : function(){
1704         
1705         var cfg = {
1706             tag : this.tag || 'div',
1707             html : '',
1708             cls : ''
1709         };
1710         if (this.jumbotron) {
1711             cfg.cls = 'jumbotron';
1712         }
1713         
1714         
1715         
1716         // - this is applied by the parent..
1717         //if (this.cls) {
1718         //    cfg.cls = this.cls + '';
1719         //}
1720         
1721         if (this.sticky.length) {
1722             
1723             var bd = Roo.get(document.body);
1724             if (!bd.hasClass('bootstrap-sticky')) {
1725                 bd.addClass('bootstrap-sticky');
1726                 Roo.select('html',true).setStyle('height', '100%');
1727             }
1728              
1729             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1730         }
1731         
1732         
1733         if (this.well.length) {
1734             switch (this.well) {
1735                 case 'lg':
1736                 case 'sm':
1737                     cfg.cls +=' well well-' +this.well;
1738                     break;
1739                 default:
1740                     cfg.cls +=' well';
1741                     break;
1742             }
1743         }
1744         
1745         if (this.hidden) {
1746             cfg.cls += ' hidden';
1747         }
1748         
1749         
1750         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1751             cfg.cls +=' alert alert-' + this.alert;
1752         }
1753         
1754         var body = cfg;
1755         
1756         if (this.panel.length) {
1757             cfg.cls += ' panel panel-' + this.panel;
1758             cfg.cn = [];
1759             if (this.header.length) {
1760                 
1761                 var h = [];
1762                 
1763                 if(this.expandable){
1764                     
1765                     cfg.cls = cfg.cls + ' expandable';
1766                     
1767                     h.push({
1768                         tag: 'i',
1769                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1770                     });
1771                     
1772                 }
1773                 
1774                 h.push(
1775                     {
1776                         tag: 'span',
1777                         cls : 'panel-title',
1778                         html : (this.expandable ? '&nbsp;' : '') + this.header
1779                     },
1780                     {
1781                         tag: 'span',
1782                         cls: 'panel-header-right',
1783                         html: this.rheader
1784                     }
1785                 );
1786                 
1787                 cfg.cn.push({
1788                     cls : 'panel-heading',
1789                     style : this.expandable ? 'cursor: pointer' : '',
1790                     cn : h
1791                 });
1792                 
1793             }
1794             
1795             body = false;
1796             cfg.cn.push({
1797                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1798                 html : this.html
1799             });
1800             
1801             
1802             if (this.footer.length) {
1803                 cfg.cn.push({
1804                     cls : 'panel-footer',
1805                     html : this.footer
1806                     
1807                 });
1808             }
1809             
1810         }
1811         
1812         if (body) {
1813             body.html = this.html || cfg.html;
1814             // prefix with the icons..
1815             if (this.fa) {
1816                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1817             }
1818             if (this.icon) {
1819                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1820             }
1821             
1822             
1823         }
1824         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1825             cfg.cls =  'container';
1826         }
1827         
1828         return cfg;
1829     },
1830     
1831     initEvents: function() 
1832     {
1833         if(this.expandable){
1834             var headerEl = this.headerEl();
1835         
1836             if(headerEl){
1837                 headerEl.on('click', this.onToggleClick, this);
1838             }
1839         }
1840         
1841         if(this.clickable){
1842             this.el.on('click', this.onClick, this);
1843         }
1844         
1845     },
1846     
1847     onToggleClick : function()
1848     {
1849         var headerEl = this.headerEl();
1850         
1851         if(!headerEl){
1852             return;
1853         }
1854         
1855         if(this.expanded){
1856             this.collapse();
1857             return;
1858         }
1859         
1860         this.expand();
1861     },
1862     
1863     expand : function()
1864     {
1865         if(this.fireEvent('expand', this)) {
1866             
1867             this.expanded = true;
1868             
1869             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1870             
1871             this.el.select('.panel-body',true).first().removeClass('hide');
1872             
1873             var toggleEl = this.toggleEl();
1874
1875             if(!toggleEl){
1876                 return;
1877             }
1878
1879             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1880         }
1881         
1882     },
1883     
1884     collapse : function()
1885     {
1886         if(this.fireEvent('collapse', this)) {
1887             
1888             this.expanded = false;
1889             
1890             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1891             this.el.select('.panel-body',true).first().addClass('hide');
1892         
1893             var toggleEl = this.toggleEl();
1894
1895             if(!toggleEl){
1896                 return;
1897             }
1898
1899             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1900         }
1901     },
1902     
1903     toggleEl : function()
1904     {
1905         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-heading .fa',true).first();
1910     },
1911     
1912     headerEl : function()
1913     {
1914         if(!this.el || !this.panel.length || !this.header.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-heading',true).first()
1919     },
1920     
1921     bodyEl : function()
1922     {
1923         if(!this.el || !this.panel.length){
1924             return;
1925         }
1926         
1927         return this.el.select('.panel-body',true).first()
1928     },
1929     
1930     titleEl : function()
1931     {
1932         if(!this.el || !this.panel.length || !this.header.length){
1933             return;
1934         }
1935         
1936         return this.el.select('.panel-title',true).first();
1937     },
1938     
1939     setTitle : function(v)
1940     {
1941         var titleEl = this.titleEl();
1942         
1943         if(!titleEl){
1944             return;
1945         }
1946         
1947         titleEl.dom.innerHTML = v;
1948     },
1949     
1950     getTitle : function()
1951     {
1952         
1953         var titleEl = this.titleEl();
1954         
1955         if(!titleEl){
1956             return '';
1957         }
1958         
1959         return titleEl.dom.innerHTML;
1960     },
1961     
1962     setRightTitle : function(v)
1963     {
1964         var t = this.el.select('.panel-header-right',true).first();
1965         
1966         if(!t){
1967             return;
1968         }
1969         
1970         t.dom.innerHTML = v;
1971     },
1972     
1973     onClick : function(e)
1974     {
1975         e.preventDefault();
1976         
1977         this.fireEvent('click', this, e);
1978     }
1979 });
1980
1981  /**
1982  * @class Roo.bootstrap.Card
1983  * @extends Roo.bootstrap.Component
1984  * @children Roo.bootstrap.Component
1985  * @licence LGPL
1986  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1987  *
1988  *
1989  * possible... may not be implemented..
1990  * @cfg {String} header_image  src url of image.
1991  * @cfg {String|Object} header
1992  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1993  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1994  * 
1995  * @cfg {String} title
1996  * @cfg {String} subtitle
1997  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1998  * @cfg {String} footer
1999  
2000  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2001  * 
2002  * @cfg {String} margin (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2004  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2005  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2006  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2007  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2008  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2009  *
2010  * @cfg {String} padding (0|1|2|3|4|5)
2011  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2012  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2013  * @cfg {String} padding_left (0|1|2|3|4|5)
2014  * @cfg {String} padding_right (0|1|2|3|4|5)
2015  * @cfg {String} padding_x (0|1|2|3|4|5)
2016  * @cfg {String} padding_y (0|1|2|3|4|5)
2017  *
2018  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2020  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2021  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2022  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023  
2024  * @config {Boolean} dragable  if this card can be dragged.
2025  * @config {String} drag_group  group for drag
2026  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2027  * @config {String} drop_group  group for drag
2028  * 
2029  * @config {Boolean} collapsable can the body be collapsed.
2030  * @config {Boolean} collapsed is the body collapsed when rendered...
2031  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2032  * @config {Boolean} rotated is the body rotated when rendered...
2033  * 
2034  * @constructor
2035  * Create a new Container
2036  * @param {Object} config The config object
2037  */
2038
2039 Roo.bootstrap.Card = function(config){
2040     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2041     
2042     this.addEvents({
2043          // raw events
2044         /**
2045          * @event drop
2046          * When a element a card is dropped
2047          * @param {Roo.bootstrap.Card} this
2048          *
2049          * 
2050          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2051          * @param {String} position 'above' or 'below'
2052          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2053         
2054          */
2055         'drop' : true,
2056          /**
2057          * @event rotate
2058          * When a element a card is rotate
2059          * @param {Roo.bootstrap.Card} this
2060          * @param {Roo.Element} n the node being dropped?
2061          * @param {Boolean} rotate status
2062          */
2063         'rotate' : true,
2064         /**
2065          * @event cardover
2066          * When a card element is dragged over ready to drop (return false to block dropable)
2067          * @param {Roo.bootstrap.Card} this
2068          * @param {Object} data from dragdrop 
2069          */
2070          'cardover' : true
2071          
2072     });
2073 };
2074
2075
2076 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2077     
2078     
2079     weight : '',
2080     
2081     margin: '', /// may be better in component?
2082     margin_top: '', 
2083     margin_bottom: '', 
2084     margin_left: '',
2085     margin_right: '',
2086     margin_x: '',
2087     margin_y: '',
2088     
2089     padding : '',
2090     padding_top: '', 
2091     padding_bottom: '', 
2092     padding_left: '',
2093     padding_right: '',
2094     padding_x: '',
2095     padding_y: '',
2096     
2097     display: '', 
2098     display_xs: '', 
2099     display_sm: '', 
2100     display_lg: '',
2101     display_xl: '',
2102  
2103     header_image  : '',
2104     header : '',
2105     header_size : 0,
2106     title : '',
2107     subtitle : '',
2108     html : '',
2109     footer: '',
2110
2111     collapsable : false,
2112     collapsed : false,
2113     rotateable : false,
2114     rotated : false,
2115     
2116     dragable : false,
2117     drag_group : false,
2118     dropable : false,
2119     drop_group : false,
2120     childContainer : false,
2121     dropEl : false, /// the dom placeholde element that indicates drop location.
2122     containerEl: false, // body container
2123     bodyEl: false, // card-body
2124     headerContainerEl : false, //
2125     headerEl : false,
2126     header_imageEl : false,
2127     
2128     
2129     layoutCls : function()
2130     {
2131         var cls = '';
2132         var t = this;
2133         Roo.log(this.margin_bottom.length);
2134         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2135             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2136             
2137             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2139             }
2140             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2142             }
2143         });
2144         
2145         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2146             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2147                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2148             }
2149         });
2150         
2151         // more generic support?
2152         if (this.hidden) {
2153             cls += ' d-none';
2154         }
2155         
2156         return cls;
2157     },
2158  
2159        // Roo.log("Call onRender: " + this.xtype);
2160         /*  We are looking at something like this.
2161 <div class="card">
2162     <img src="..." class="card-img-top" alt="...">
2163     <div class="card-body">
2164         <h5 class="card-title">Card title</h5>
2165          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2166
2167         >> this bit is really the body...
2168         <div> << we will ad dthis in hopefully it will not break shit.
2169         
2170         ** card text does not actually have any styling...
2171         
2172             <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>
2173         
2174         </div> <<
2175           <a href="#" class="card-link">Card link</a>
2176           
2177     </div>
2178     <div class="card-footer">
2179         <small class="text-muted">Last updated 3 mins ago</small>
2180     </div>
2181 </div>
2182          */
2183     getAutoCreate : function(){
2184         
2185         var cfg = {
2186             tag : 'div',
2187             cls : 'card',
2188             cn : [ ]
2189         };
2190         
2191         if (this.weight.length && this.weight != 'light') {
2192             cfg.cls += ' text-white';
2193         } else {
2194             cfg.cls += ' text-dark'; // need as it's nested..
2195         }
2196         if (this.weight.length) {
2197             cfg.cls += ' bg-' + this.weight;
2198         }
2199         
2200         cfg.cls += ' ' + this.layoutCls(); 
2201         
2202         var hdr = false;
2203         var hdr_ctr = false;
2204         if (this.header.length) {
2205             hdr = {
2206                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2207                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208                 cn : []
2209             };
2210             cfg.cn.push(hdr);
2211             hdr_ctr = hdr;
2212         } else {
2213             hdr = {
2214                 tag : 'div',
2215                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2216                 cn : []
2217             };
2218             cfg.cn.push(hdr);
2219             hdr_ctr = hdr;
2220         }
2221         if (this.collapsable) {
2222             hdr_ctr = {
2223             tag : 'a',
2224             cls : 'd-block user-select-none',
2225             cn: [
2226                     {
2227                         tag: 'i',
2228                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2229                     }
2230                    
2231                 ]
2232             };
2233             hdr.cn.push(hdr_ctr);
2234         }
2235         
2236         hdr_ctr.cn.push(        {
2237             tag: 'span',
2238             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2239             html : this.header
2240         });
2241         
2242         
2243         if (this.header_image.length) {
2244             cfg.cn.push({
2245                 tag : 'img',
2246                 cls : 'card-img-top',
2247                 src: this.header_image // escape?
2248             });
2249         } else {
2250             cfg.cn.push({
2251                     tag : 'div',
2252                     cls : 'card-img-top d-none' 
2253                 });
2254         }
2255             
2256         var body = {
2257             tag : 'div',
2258             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2259             cn : []
2260         };
2261         var obody = body;
2262         if (this.collapsable || this.rotateable) {
2263             obody = {
2264                 tag: 'div',
2265                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2266                 cn : [  body ]
2267             };
2268         }
2269         
2270         cfg.cn.push(obody);
2271         
2272         if (this.title.length) {
2273             body.cn.push({
2274                 tag : 'div',
2275                 cls : 'card-title',
2276                 src: this.title // escape?
2277             });
2278         }  
2279         
2280         if (this.subtitle.length) {
2281             body.cn.push({
2282                 tag : 'div',
2283                 cls : 'card-title',
2284                 src: this.subtitle // escape?
2285             });
2286         }
2287         
2288         body.cn.push({
2289             tag : 'div',
2290             cls : 'roo-card-body-ctr'
2291         });
2292         
2293         if (this.html.length) {
2294             body.cn.push({
2295                 tag: 'div',
2296                 html : this.html
2297             });
2298         }
2299         // fixme ? handle objects?
2300         
2301         if (this.footer.length) {
2302            
2303             cfg.cn.push({
2304                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2305                 html : this.footer
2306             });
2307             
2308         } else {
2309             cfg.cn.push({cls : 'card-footer d-none'});
2310         }
2311         
2312         // footer...
2313         
2314         return cfg;
2315     },
2316     
2317     
2318     getCardHeader : function()
2319     {
2320         var  ret = this.el.select('.card-header',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardFooter : function()
2328     {
2329         var  ret = this.el.select('.card-footer',true).first();
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333         
2334         return ret;
2335     },
2336     getCardImageTop : function()
2337     {
2338         var  ret = this.header_imageEl;
2339         if (ret.hasClass('d-none')) {
2340             ret.removeClass('d-none');
2341         }
2342             
2343         return ret;
2344     },
2345     
2346     getChildContainer : function()
2347     {
2348         
2349         if(!this.el){
2350             return false;
2351         }
2352         return this.el.select('.roo-card-body-ctr',true).first();    
2353     },
2354     
2355     initEvents: function() 
2356     {
2357         this.bodyEl = this.el.select('.card-body',true).first(); 
2358         this.containerEl = this.getChildContainer();
2359         if(this.dragable){
2360             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2361                     containerScroll: true,
2362                     ddGroup: this.drag_group || 'default_card_drag_group'
2363             });
2364             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2365         }
2366         if (this.dropable) {
2367             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2368                 containerScroll: true,
2369                 ddGroup: this.drop_group || 'default_card_drag_group'
2370             });
2371             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2372             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2373             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2374             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2375             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2376         }
2377         
2378         if (this.collapsable) {
2379             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2380         }
2381         if (this.rotateable) {
2382             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2383         }
2384         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2385          
2386         this.footerEl = this.el.select('.card-footer',true).first();
2387         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2388         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2389         this.headerEl = this.el.select('.card-header',true).first();
2390         
2391         if (this.rotated) {
2392             this.el.addClass('roo-card-rotated');
2393             this.fireEvent('rotate', this, true);
2394         }
2395         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2396         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2397         
2398     },
2399     getDragData : function(e)
2400     {
2401         var target = this.getEl();
2402         if (target) {
2403             //this.handleSelection(e);
2404             
2405             var dragData = {
2406                 source: this,
2407                 copy: false,
2408                 nodes: this.getEl(),
2409                 records: []
2410             };
2411             
2412             
2413             dragData.ddel = target.dom ;    // the div element
2414             Roo.log(target.getWidth( ));
2415             dragData.ddel.style.width = target.getWidth() + 'px';
2416             
2417             return dragData;
2418         }
2419         return false;
2420     },
2421     /**
2422     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2423     *    whole Element becomes the target, and this causes the drop gesture to append.
2424     *
2425     *    Returns an object:
2426     *     {
2427            
2428            position : 'below' or 'above'
2429            card  : relateive to card OBJECT (or true for no cards listed)
2430            items_n : relative to nth item in list
2431            card_n : relative to  nth card in list
2432     }
2433     *
2434     *    
2435     */
2436     getTargetFromEvent : function(e, dragged_card_el)
2437     {
2438         var target = e.getTarget();
2439         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2440             target = target.parentNode;
2441         }
2442         
2443         var ret = {
2444             position: '',
2445             cards : [],
2446             card_n : -1,
2447             items_n : -1,
2448             card : false 
2449         };
2450         
2451         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2452         // see if target is one of the 'cards'...
2453         
2454         
2455         //Roo.log(this.items.length);
2456         var pos = false;
2457         
2458         var last_card_n = 0;
2459         var cards_len  = 0;
2460         for (var i = 0;i< this.items.length;i++) {
2461             
2462             if (!this.items[i].el.hasClass('card')) {
2463                  continue;
2464             }
2465             pos = this.getDropPoint(e, this.items[i].el.dom);
2466             
2467             cards_len = ret.cards.length;
2468             //Roo.log(this.items[i].el.dom.id);
2469             ret.cards.push(this.items[i]);
2470             last_card_n  = i;
2471             if (ret.card_n < 0 && pos == 'above') {
2472                 ret.position = cards_len > 0 ? 'below' : pos;
2473                 ret.items_n = i > 0 ? i - 1 : 0;
2474                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2475                 ret.card = ret.cards[ret.card_n];
2476             }
2477         }
2478         if (!ret.cards.length) {
2479             ret.card = true;
2480             ret.position = 'below';
2481             ret.items_n;
2482             return ret;
2483         }
2484         // could not find a card.. stick it at the end..
2485         if (ret.card_n < 0) {
2486             ret.card_n = last_card_n;
2487             ret.card = ret.cards[last_card_n];
2488             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2489             ret.position = 'below';
2490         }
2491         
2492         if (this.items[ret.items_n].el == dragged_card_el) {
2493             return false;
2494         }
2495         
2496         if (ret.position == 'below') {
2497             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2498             
2499             if (card_after  && card_after.el == dragged_card_el) {
2500                 return false;
2501             }
2502             return ret;
2503         }
2504         
2505         // its's after ..
2506         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2507         
2508         if (card_before  && card_before.el == dragged_card_el) {
2509             return false;
2510         }
2511         
2512         return ret;
2513     },
2514     
2515     onNodeEnter : function(n, dd, e, data){
2516         return false;
2517     },
2518     onNodeOver : function(n, dd, e, data)
2519     {
2520        
2521         var target_info = this.getTargetFromEvent(e,data.source.el);
2522         if (target_info === false) {
2523             this.dropPlaceHolder('hide');
2524             return false;
2525         }
2526         Roo.log(['getTargetFromEvent', target_info ]);
2527         
2528         
2529         if (this.fireEvent('cardover', this, [ data ]) === false) {
2530             return false;
2531         }
2532         
2533         this.dropPlaceHolder('show', target_info,data);
2534         
2535         return false; 
2536     },
2537     onNodeOut : function(n, dd, e, data){
2538         this.dropPlaceHolder('hide');
2539      
2540     },
2541     onNodeDrop : function(n, dd, e, data)
2542     {
2543         
2544         // call drop - return false if
2545         
2546         // this could actually fail - if the Network drops..
2547         // we will ignore this at present..- client should probably reload
2548         // the whole set of cards if stuff like that fails.
2549         
2550         
2551         var info = this.getTargetFromEvent(e,data.source.el);
2552         if (info === false) {
2553             return false;
2554         }
2555         this.dropPlaceHolder('hide');
2556   
2557           
2558     
2559         this.acceptCard(data.source, info.position, info.card, info.items_n);
2560         return true;
2561          
2562     },
2563     firstChildCard : function()
2564     {
2565         for (var i = 0;i< this.items.length;i++) {
2566             
2567             if (!this.items[i].el.hasClass('card')) {
2568                  continue;
2569             }
2570             return this.items[i];
2571         }
2572         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2573     },
2574     /**
2575      * accept card
2576      *
2577      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2578      */
2579     acceptCard : function(move_card,  position, next_to_card )
2580     {
2581         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2582             return false;
2583         }
2584         
2585         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2586         
2587         move_card.parent().removeCard(move_card);
2588         
2589         
2590         var dom = move_card.el.dom;
2591         dom.style.width = ''; // clear with - which is set by drag.
2592         
2593         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2594             var cardel = next_to_card.el.dom;
2595             
2596             if (position == 'above' ) {
2597                 cardel.parentNode.insertBefore(dom, cardel);
2598             } else if (cardel.nextSibling) {
2599                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2600             } else {
2601                 cardel.parentNode.append(dom);
2602             }
2603         } else {
2604             // card container???
2605             this.containerEl.dom.append(dom);
2606         }
2607         
2608         //FIXME HANDLE card = true 
2609         
2610         // add this to the correct place in items.
2611         
2612         // remove Card from items.
2613         
2614        
2615         if (this.items.length) {
2616             var nitems = [];
2617             //Roo.log([info.items_n, info.position, this.items.length]);
2618             for (var i =0; i < this.items.length; i++) {
2619                 if (i == to_items_n && position == 'above') {
2620                     nitems.push(move_card);
2621                 }
2622                 nitems.push(this.items[i]);
2623                 if (i == to_items_n && position == 'below') {
2624                     nitems.push(move_card);
2625                 }
2626             }
2627             this.items = nitems;
2628             Roo.log(this.items);
2629         } else {
2630             this.items.push(move_card);
2631         }
2632         
2633         move_card.parentId = this.id;
2634         
2635         return true;
2636         
2637         
2638     },
2639     removeCard : function(c)
2640     {
2641         this.items = this.items.filter(function(e) { return e != c });
2642  
2643         var dom = c.el.dom;
2644         dom.parentNode.removeChild(dom);
2645         dom.style.width = ''; // clear with - which is set by drag.
2646         c.parentId = false;
2647         
2648     },
2649     
2650     /**    Decide whether to drop above or below a View node. */
2651     getDropPoint : function(e, n, dd)
2652     {
2653         if (dd) {
2654              return false;
2655         }
2656         if (n == this.containerEl.dom) {
2657             return "above";
2658         }
2659         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2660         var c = t + (b - t) / 2;
2661         var y = Roo.lib.Event.getPageY(e);
2662         if(y <= c) {
2663             return "above";
2664         }else{
2665             return "below";
2666         }
2667     },
2668     onToggleCollapse : function(e)
2669         {
2670         if (this.collapsed) {
2671             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2672             this.collapsableEl.addClass('show');
2673             this.collapsed = false;
2674             return;
2675         }
2676         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2677         this.collapsableEl.removeClass('show');
2678         this.collapsed = true;
2679         
2680     
2681     },
2682     
2683     onToggleRotate : function(e)
2684     {
2685         this.collapsableEl.removeClass('show');
2686         this.footerEl.removeClass('d-none');
2687         this.el.removeClass('roo-card-rotated');
2688         this.el.removeClass('d-none');
2689         if (this.rotated) {
2690             
2691             this.collapsableEl.addClass('show');
2692             this.rotated = false;
2693             this.fireEvent('rotate', this, this.rotated);
2694             return;
2695         }
2696         this.el.addClass('roo-card-rotated');
2697         this.footerEl.addClass('d-none');
2698         this.el.select('.roo-collapsable').removeClass('show');
2699         
2700         this.rotated = true;
2701         this.fireEvent('rotate', this, this.rotated);
2702     
2703     },
2704     
2705     dropPlaceHolder: function (action, info, data)
2706     {
2707         if (this.dropEl === false) {
2708             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2709             cls : 'd-none'
2710             },true);
2711         }
2712         this.dropEl.removeClass(['d-none', 'd-block']);        
2713         if (action == 'hide') {
2714             
2715             this.dropEl.addClass('d-none');
2716             return;
2717         }
2718         // FIXME - info.card == true!!!
2719         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2720         
2721         if (info.card !== true) {
2722             var cardel = info.card.el.dom;
2723             
2724             if (info.position == 'above') {
2725                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2726             } else if (cardel.nextSibling) {
2727                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2728             } else {
2729                 cardel.parentNode.append(this.dropEl.dom);
2730             }
2731         } else {
2732             // card container???
2733             this.containerEl.dom.append(this.dropEl.dom);
2734         }
2735         
2736         this.dropEl.addClass('d-block roo-card-dropzone');
2737         
2738         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2739         
2740         
2741     
2742     
2743     
2744     },
2745     setHeaderText: function(html)
2746     {
2747         this.header = html;
2748         if (this.headerContainerEl) {
2749             this.headerContainerEl.dom.innerHTML = html;
2750         }
2751     },
2752     onHeaderImageLoad : function(ev, he)
2753     {
2754         if (!this.header_image_fit_square) {
2755             return;
2756         }
2757         
2758         var hw = he.naturalHeight / he.naturalWidth;
2759         // wide image = < 0
2760         // tall image = > 1
2761         //var w = he.dom.naturalWidth;
2762         var ww = he.width;
2763         he.style.left =  0;
2764         he.style.position =  'relative';
2765         if (hw > 1) {
2766             var nw = (ww * (1/hw));
2767             Roo.get(he).setSize( ww * (1/hw),  ww);
2768             he.style.left =  ((ww - nw)/ 2) + 'px';
2769             he.style.position =  'relative';
2770         }
2771
2772     }
2773
2774     
2775 });
2776
2777 /*
2778  * - LGPL
2779  *
2780  * Card header - holder for the card header elements.
2781  * 
2782  */
2783
2784 /**
2785  * @class Roo.bootstrap.CardHeader
2786  * @extends Roo.bootstrap.Element
2787  * @parent Roo.bootstrap.Card
2788  * @children Roo.bootstrap.Component
2789  * Bootstrap CardHeader class
2790  * @constructor
2791  * Create a new Card Header - that you can embed children into
2792  * @param {Object} config The config object
2793  */
2794
2795 Roo.bootstrap.CardHeader = function(config){
2796     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2797 };
2798
2799 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2800     
2801     
2802     container_method : 'getCardHeader' 
2803     
2804      
2805     
2806     
2807    
2808 });
2809
2810  
2811
2812  /*
2813  * - LGPL
2814  *
2815  * Card footer - holder for the card footer elements.
2816  * 
2817  */
2818
2819 /**
2820  * @class Roo.bootstrap.CardFooter
2821  * @extends Roo.bootstrap.Element
2822  * @parent Roo.bootstrap.Card
2823  * @children Roo.bootstrap.Component
2824  * Bootstrap CardFooter class
2825  * 
2826  * @constructor
2827  * Create a new Card Footer - that you can embed children into
2828  * @param {Object} config The config object
2829  */
2830
2831 Roo.bootstrap.CardFooter = function(config){
2832     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2833 };
2834
2835 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2836     
2837     
2838     container_method : 'getCardFooter' 
2839     
2840      
2841     
2842     
2843    
2844 });
2845
2846  
2847
2848  /*
2849  * - LGPL
2850  *
2851  * Card header - holder for the card header elements.
2852  * 
2853  */
2854
2855 /**
2856  * @class Roo.bootstrap.CardImageTop
2857  * @extends Roo.bootstrap.Element
2858  * @parent Roo.bootstrap.Card
2859  * @children Roo.bootstrap.Component
2860  * Bootstrap CardImageTop class
2861  * 
2862  * @constructor
2863  * Create a new Card Image Top container
2864  * @param {Object} config The config object
2865  */
2866
2867 Roo.bootstrap.CardImageTop = function(config){
2868     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2869 };
2870
2871 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2872     
2873    
2874     container_method : 'getCardImageTop' 
2875     
2876      
2877     
2878    
2879 });
2880
2881  
2882
2883  
2884 /*
2885 * Licence: LGPL
2886 */
2887
2888 /**
2889  * @class Roo.bootstrap.ButtonUploader
2890  * @extends Roo.bootstrap.Button
2891  * Bootstrap Button Uploader class - it's a button which when you add files to it
2892  *
2893  * 
2894  * @cfg {Number} errorTimeout default 3000
2895  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2896  * @cfg {Array}  html The button text.
2897  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2898  *
2899  * @constructor
2900  * Create a new CardUploader
2901  * @param {Object} config The config object
2902  */
2903
2904 Roo.bootstrap.ButtonUploader = function(config){
2905     
2906  
2907     
2908     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2909     
2910      
2911      this.addEvents({
2912          // raw events
2913         /**
2914          * @event beforeselect
2915          * When button is pressed, before show upload files dialog is shown
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          *
2918          */
2919         'beforeselect' : true,
2920          /**
2921          * @event fired when files have been selected, 
2922          * When a the download link is clicked
2923          * @param {Roo.bootstrap.UploaderButton} this
2924          * @param {Array} Array of files that have been uploaded
2925          */
2926         'uploaded' : true
2927         
2928     });
2929 };
2930  
2931 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2932     
2933      
2934     errorTimeout : 3000,
2935      
2936     images : false,
2937    
2938     fileCollection : false,
2939     allowBlank : true,
2940     
2941     multiple : true,
2942     
2943     getAutoCreate : function()
2944     {
2945        
2946         
2947         return  {
2948             cls :'div' ,
2949             cn : [
2950                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2951             ]
2952         };
2953            
2954          
2955     },
2956      
2957    
2958     initEvents : function()
2959     {
2960         
2961         Roo.bootstrap.Button.prototype.initEvents.call(this);
2962         
2963         
2964         
2965         
2966         
2967         this.urlAPI = (window.createObjectURL && window) || 
2968                                 (window.URL && URL.revokeObjectURL && URL) || 
2969                                 (window.webkitURL && webkitURL);
2970                         
2971         var im = {
2972             tag: 'input',
2973             type : 'file',
2974             cls : 'd-none  roo-card-upload-selector' 
2975           
2976         };
2977         if (this.multiple) {
2978             im.multiple = 'multiple';
2979         }
2980         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2981        
2982         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2983         
2984         this.selectorEl.on('change', this.onFileSelected, this);
2985          
2986          
2987        
2988     },
2989     
2990    
2991     onClick : function(e)
2992     {
2993         e.preventDefault();
2994         
2995         if ( this.fireEvent('beforeselect', this) === false) {
2996             return;
2997         }
2998          
2999         this.selectorEl.dom.click();
3000          
3001     },
3002     
3003     onFileSelected : function(e)
3004     {
3005         e.preventDefault();
3006         
3007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3008             return;
3009         }
3010         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3011         this.selectorEl.dom.value  = '';// hopefully reset..
3012         
3013         this.fireEvent('uploaded', this,  files );
3014         
3015     },
3016     
3017        
3018    
3019     
3020     /**
3021      * addCard - add an Attachment to the uploader
3022      * @param data - the data about the image to upload
3023      *
3024      * {
3025           id : 123
3026           title : "Title of file",
3027           is_uploaded : false,
3028           src : "http://.....",
3029           srcfile : { the File upload object },
3030           mimetype : file.type,
3031           preview : false,
3032           is_deleted : 0
3033           .. any other data...
3034         }
3035      *
3036      * 
3037     */
3038      
3039     reset: function()
3040     {
3041          
3042          this.selectorEl
3043     } 
3044     
3045     
3046     
3047     
3048 });
3049  /*
3050  * - LGPL
3051  *
3052  * image
3053  * 
3054  */
3055
3056
3057 /**
3058  * @class Roo.bootstrap.Img
3059  * @extends Roo.bootstrap.Component
3060  * Bootstrap Img class
3061  * @cfg {Boolean} imgResponsive false | true
3062  * @cfg {String} border rounded | circle | thumbnail
3063  * @cfg {String} src image source
3064  * @cfg {String} alt image alternative text
3065  * @cfg {String} href a tag href
3066  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3067  * @cfg {String} xsUrl xs image source
3068  * @cfg {String} smUrl sm image source
3069  * @cfg {String} mdUrl md image source
3070  * @cfg {String} lgUrl lg image source
3071  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3072  * 
3073  * @constructor
3074  * Create a new Input
3075  * @param {Object} config The config object
3076  */
3077
3078 Roo.bootstrap.Img = function(config){
3079     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080     
3081     this.addEvents({
3082         // img events
3083         /**
3084          * @event click
3085          * The img click event for the img.
3086          * @param {Roo.EventObject} e
3087          */
3088         "click" : true,
3089         /**
3090          * @event load
3091          * The when any image loads
3092          * @param {Roo.EventObject} e
3093          */
3094         "load" : true
3095     });
3096 };
3097
3098 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3099     
3100     imgResponsive: true,
3101     border: '',
3102     src: 'about:blank',
3103     href: false,
3104     target: false,
3105     xsUrl: '',
3106     smUrl: '',
3107     mdUrl: '',
3108     lgUrl: '',
3109     backgroundContain : false,
3110
3111     getAutoCreate : function()
3112     {   
3113         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3114             return this.createSingleImg();
3115         }
3116         
3117         var cfg = {
3118             tag: 'div',
3119             cls: 'roo-image-responsive-group',
3120             cn: []
3121         };
3122         var _this = this;
3123         
3124         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3125             
3126             if(!_this[size + 'Url']){
3127                 return;
3128             }
3129             
3130             var img = {
3131                 tag: 'img',
3132                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3133                 html: _this.html || cfg.html,
3134                 src: _this[size + 'Url']
3135             };
3136             
3137             img.cls += ' roo-image-responsive-' + size;
3138             
3139             var s = ['xs', 'sm', 'md', 'lg'];
3140             
3141             s.splice(s.indexOf(size), 1);
3142             
3143             Roo.each(s, function(ss){
3144                 img.cls += ' hidden-' + ss;
3145             });
3146             
3147             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3148                 cfg.cls += ' img-' + _this.border;
3149             }
3150             
3151             if(_this.alt){
3152                 cfg.alt = _this.alt;
3153             }
3154             
3155             if(_this.href){
3156                 var a = {
3157                     tag: 'a',
3158                     href: _this.href,
3159                     cn: [
3160                         img
3161                     ]
3162                 };
3163
3164                 if(this.target){
3165                     a.target = _this.target;
3166                 }
3167             }
3168             
3169             cfg.cn.push((_this.href) ? a : img);
3170             
3171         });
3172         
3173         return cfg;
3174     },
3175     
3176     createSingleImg : function()
3177     {
3178         var cfg = {
3179             tag: 'img',
3180             cls: (this.imgResponsive) ? 'img-responsive' : '',
3181             html : null,
3182             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3183         };
3184         
3185         if (this.backgroundContain) {
3186             cfg.cls += ' background-contain';
3187         }
3188         
3189         cfg.html = this.html || cfg.html;
3190         
3191         if (this.backgroundContain) {
3192             cfg.style="background-image: url(" + this.src + ')';
3193         } else {
3194             cfg.src = this.src || cfg.src;
3195         }
3196         
3197         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3198             cfg.cls += ' img-' + this.border;
3199         }
3200         
3201         if(this.alt){
3202             cfg.alt = this.alt;
3203         }
3204         
3205         if(this.href){
3206             var a = {
3207                 tag: 'a',
3208                 href: this.href,
3209                 cn: [
3210                     cfg
3211                 ]
3212             };
3213             
3214             if(this.target){
3215                 a.target = this.target;
3216             }
3217             
3218         }
3219         
3220         return (this.href) ? a : cfg;
3221     },
3222     
3223     initEvents: function() 
3224     {
3225         if(!this.href){
3226             this.el.on('click', this.onClick, this);
3227         }
3228         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3229             this.el.on('load', this.onImageLoad, this);
3230         } else {
3231             // not sure if this works.. not tested
3232             this.el.select('img', true).on('load', this.onImageLoad, this);
3233         }
3234         
3235     },
3236     
3237     onClick : function(e)
3238     {
3239         Roo.log('img onclick');
3240         this.fireEvent('click', this, e);
3241     },
3242     onImageLoad: function(e)
3243     {
3244         Roo.log('img load');
3245         this.fireEvent('load', this, e);
3246     },
3247     
3248     /**
3249      * Sets the url of the image - used to update it
3250      * @param {String} url the url of the image
3251      */
3252     
3253     setSrc : function(url)
3254     {
3255         this.src =  url;
3256         
3257         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3258             if (this.backgroundContain) {
3259                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3260             } else {
3261                 this.el.dom.src =  url;
3262             }
3263             return;
3264         }
3265         
3266         this.el.select('img', true).first().dom.src =  url;
3267     }
3268     
3269     
3270    
3271 });
3272
3273  /*
3274  * - LGPL
3275  *
3276  * image
3277  * 
3278  */
3279
3280
3281 /**
3282  * @class Roo.bootstrap.Link
3283  * @extends Roo.bootstrap.Component
3284  * @children Roo.bootstrap.Component
3285  * Bootstrap Link Class (eg. '<a href>')
3286  
3287  * @cfg {String} alt image alternative text
3288  * @cfg {String} href a tag href
3289  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3290  * @cfg {String} html the content of the link.
3291  * @cfg {String} anchor name for the anchor link
3292  * @cfg {String} fa - favicon
3293
3294  * @cfg {Boolean} preventDefault (true | false) default false
3295
3296  * 
3297  * @constructor
3298  * Create a new Input
3299  * @param {Object} config The config object
3300  */
3301
3302 Roo.bootstrap.Link = function(config){
3303     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304     
3305     this.addEvents({
3306         // img events
3307         /**
3308          * @event click
3309          * The img click event for the img.
3310          * @param {Roo.EventObject} e
3311          */
3312         "click" : true
3313     });
3314 };
3315
3316 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3317     
3318     href: false,
3319     target: false,
3320     preventDefault: false,
3321     anchor : false,
3322     alt : false,
3323     fa: false,
3324
3325
3326     getAutoCreate : function()
3327     {
3328         var html = this.html || '';
3329         
3330         if (this.fa !== false) {
3331             html = '<i class="fa fa-' + this.fa + '"></i>';
3332         }
3333         var cfg = {
3334             tag: 'a'
3335         };
3336         // anchor's do not require html/href...
3337         if (this.anchor === false) {
3338             cfg.html = html;
3339             cfg.href = this.href || '#';
3340         } else {
3341             cfg.name = this.anchor;
3342             if (this.html !== false || this.fa !== false) {
3343                 cfg.html = html;
3344             }
3345             if (this.href !== false) {
3346                 cfg.href = this.href;
3347             }
3348         }
3349         
3350         if(this.alt !== false){
3351             cfg.alt = this.alt;
3352         }
3353         
3354         
3355         if(this.target !== false) {
3356             cfg.target = this.target;
3357         }
3358         
3359         return cfg;
3360     },
3361     
3362     initEvents: function() {
3363         
3364         if(!this.href || this.preventDefault){
3365             this.el.on('click', this.onClick, this);
3366         }
3367     },
3368     
3369     onClick : function(e)
3370     {
3371         if(this.preventDefault){
3372             e.preventDefault();
3373         }
3374         //Roo.log('img onclick');
3375         this.fireEvent('click', this, e);
3376     }
3377    
3378 });
3379
3380  /*
3381  * - LGPL
3382  *
3383  * header
3384  * 
3385  */
3386
3387 /**
3388  * @class Roo.bootstrap.Header
3389  * @extends Roo.bootstrap.Component
3390  * @children Roo.bootstrap.Component
3391  * Bootstrap Header class
3392  *
3393  * 
3394  * @cfg {String} html content of header
3395  * @cfg {Number} level (1|2|3|4|5|6) default 1
3396  * 
3397  * @constructor
3398  * Create a new Header
3399  * @param {Object} config The config object
3400  */
3401
3402
3403 Roo.bootstrap.Header  = function(config){
3404     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3405 };
3406
3407 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3408     
3409     //href : false,
3410     html : false,
3411     level : 1,
3412     
3413     
3414     
3415     getAutoCreate : function(){
3416         
3417         
3418         
3419         var cfg = {
3420             tag: 'h' + (1 *this.level),
3421             html: this.html || ''
3422         } ;
3423         
3424         return cfg;
3425     }
3426    
3427 });
3428
3429  
3430
3431  /**
3432  * @class Roo.bootstrap.MenuMgr
3433  * @licence LGPL
3434  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3435  * @static
3436  */
3437 Roo.bootstrap.menu.Manager = function(){
3438    var menus, active, groups = {}, attached = false, lastShow = new Date();
3439
3440    // private - called when first menu is created
3441    function init(){
3442        menus = {};
3443        active = new Roo.util.MixedCollection();
3444        Roo.get(document).addKeyListener(27, function(){
3445            if(active.length > 0){
3446                hideAll();
3447            }
3448        });
3449    }
3450
3451    // private
3452    function hideAll(){
3453        if(active && active.length > 0){
3454            var c = active.clone();
3455            c.each(function(m){
3456                m.hide();
3457            });
3458        }
3459    }
3460
3461    // private
3462    function onHide(m){
3463        active.remove(m);
3464        if(active.length < 1){
3465            Roo.get(document).un("mouseup", onMouseDown);
3466             
3467            attached = false;
3468        }
3469    }
3470
3471    // private
3472    function onShow(m){
3473        var last = active.last();
3474        lastShow = new Date();
3475        active.add(m);
3476        if(!attached){
3477           Roo.get(document).on("mouseup", onMouseDown);
3478            
3479            attached = true;
3480        }
3481        if(m.parentMenu){
3482           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3483           m.parentMenu.activeChild = m;
3484        }else if(last && last.isVisible()){
3485           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3486        }
3487    }
3488
3489    // private
3490    function onBeforeHide(m){
3491        if(m.activeChild){
3492            m.activeChild.hide();
3493        }
3494        if(m.autoHideTimer){
3495            clearTimeout(m.autoHideTimer);
3496            delete m.autoHideTimer;
3497        }
3498    }
3499
3500    // private
3501    function onBeforeShow(m){
3502        var pm = m.parentMenu;
3503        if(!pm && !m.allowOtherMenus){
3504            hideAll();
3505        }else if(pm && pm.activeChild && active != m){
3506            pm.activeChild.hide();
3507        }
3508    }
3509
3510    // private this should really trigger on mouseup..
3511    function onMouseDown(e){
3512         Roo.log("on Mouse Up");
3513         
3514         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3515             Roo.log("MenuManager hideAll");
3516             hideAll();
3517             e.stopEvent();
3518         }
3519         
3520         
3521    }
3522
3523    // private
3524    function onBeforeCheck(mi, state){
3525        if(state){
3526            var g = groups[mi.group];
3527            for(var i = 0, l = g.length; i < l; i++){
3528                if(g[i] != mi){
3529                    g[i].setChecked(false);
3530                }
3531            }
3532        }
3533    }
3534
3535    return {
3536
3537        /**
3538         * Hides all menus that are currently visible
3539         */
3540        hideAll : function(){
3541             hideAll();  
3542        },
3543
3544        // private
3545        register : function(menu){
3546            if(!menus){
3547                init();
3548            }
3549            menus[menu.id] = menu;
3550            menu.on("beforehide", onBeforeHide);
3551            menu.on("hide", onHide);
3552            menu.on("beforeshow", onBeforeShow);
3553            menu.on("show", onShow);
3554            var g = menu.group;
3555            if(g && menu.events["checkchange"]){
3556                if(!groups[g]){
3557                    groups[g] = [];
3558                }
3559                groups[g].push(menu);
3560                menu.on("checkchange", onCheck);
3561            }
3562        },
3563
3564         /**
3565          * Returns a {@link Roo.menu.Menu} object
3566          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3567          * be used to generate and return a new Menu instance.
3568          */
3569        get : function(menu){
3570            if(typeof menu == "string"){ // menu id
3571                return menus[menu];
3572            }else if(menu.events){  // menu instance
3573                return menu;
3574            }
3575            /*else if(typeof menu.length == 'number'){ // array of menu items?
3576                return new Roo.bootstrap.Menu({items:menu});
3577            }else{ // otherwise, must be a config
3578                return new Roo.bootstrap.Menu(menu);
3579            }
3580            */
3581            return false;
3582        },
3583
3584        // private
3585        unregister : function(menu){
3586            delete menus[menu.id];
3587            menu.un("beforehide", onBeforeHide);
3588            menu.un("hide", onHide);
3589            menu.un("beforeshow", onBeforeShow);
3590            menu.un("show", onShow);
3591            var g = menu.group;
3592            if(g && menu.events["checkchange"]){
3593                groups[g].remove(menu);
3594                menu.un("checkchange", onCheck);
3595            }
3596        },
3597
3598        // private
3599        registerCheckable : function(menuItem){
3600            var g = menuItem.group;
3601            if(g){
3602                if(!groups[g]){
3603                    groups[g] = [];
3604                }
3605                groups[g].push(menuItem);
3606                menuItem.on("beforecheckchange", onBeforeCheck);
3607            }
3608        },
3609
3610        // private
3611        unregisterCheckable : function(menuItem){
3612            var g = menuItem.group;
3613            if(g){
3614                groups[g].remove(menuItem);
3615                menuItem.un("beforecheckchange", onBeforeCheck);
3616            }
3617        }
3618    };
3619 }(); 
3620 /**
3621  * @class Roo.bootstrap.menu.Menu
3622  * @extends Roo.bootstrap.Component
3623  * @licence LGPL
3624  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3625  * @parent none
3626  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3627  * 
3628  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3629  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3630  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3631  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3632 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3633 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3634  
3635  * @constructor
3636  * Create a new Menu
3637  * @param {Object} config The config objectQ
3638  */
3639
3640
3641 Roo.bootstrap.menu.Menu = function(config){
3642     
3643     if (config.type == 'treeview') {
3644         // normally menu's are drawn attached to the document to handle layering etc..
3645         // however treeview (used by the docs menu is drawn into the parent element)
3646         this.container_method = 'getChildContainer'; 
3647     }
3648     
3649     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3650     if (this.registerMenu && this.type != 'treeview')  {
3651         Roo.bootstrap.menu.Manager.register(this);
3652     }
3653     
3654     
3655     this.addEvents({
3656         /**
3657          * @event beforeshow
3658          * Fires before this menu is displayed (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforeshow : true,
3662         /**
3663          * @event beforehide
3664          * Fires before this menu is hidden (return false to block)
3665          * @param {Roo.menu.Menu} this
3666          */
3667         beforehide : true,
3668         /**
3669          * @event show
3670          * Fires after this menu is displayed
3671          * @param {Roo.menu.Menu} this
3672          */
3673         show : true,
3674         /**
3675          * @event hide
3676          * Fires after this menu is hidden
3677          * @param {Roo.menu.Menu} this
3678          */
3679         hide : true,
3680         /**
3681          * @event click
3682          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3683          * @param {Roo.menu.Menu} this
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          * @param {Roo.EventObject} e
3686          */
3687         click : true,
3688         /**
3689          * @event mouseover
3690          * Fires when the mouse is hovering over this menu
3691          * @param {Roo.menu.Menu} this
3692          * @param {Roo.EventObject} e
3693          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694          */
3695         mouseover : true,
3696         /**
3697          * @event mouseout
3698          * Fires when the mouse exits this menu
3699          * @param {Roo.menu.Menu} this
3700          * @param {Roo.EventObject} e
3701          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702          */
3703         mouseout : true,
3704         /**
3705          * @event itemclick
3706          * Fires when a menu item contained in this menu is clicked
3707          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3708          * @param {Roo.EventObject} e
3709          */
3710         itemclick: true
3711     });
3712     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3713 };
3714
3715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3716     
3717    /// html : false,
3718    
3719     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3720     type: false,
3721     /**
3722      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3723      */
3724     registerMenu : true,
3725     
3726     menuItems :false, // stores the menu items..
3727     
3728     hidden:true,
3729         
3730     parentMenu : false,
3731     
3732     stopEvent : true,
3733     
3734     isLink : false,
3735     
3736     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3737     
3738     hideTrigger : false,
3739     
3740     align : 'tl-bl?',
3741     
3742     
3743     getChildContainer : function() {
3744         return this.el;  
3745     },
3746     
3747     getAutoCreate : function(){
3748          
3749         //if (['right'].indexOf(this.align)!==-1) {
3750         //    cfg.cn[1].cls += ' pull-right'
3751         //}
3752          
3753         var cfg = {
3754             tag : 'ul',
3755             cls : 'dropdown-menu shadow' ,
3756             style : 'z-index:1000'
3757             
3758         };
3759         
3760         if (this.type === 'submenu') {
3761             cfg.cls = 'submenu active';
3762         }
3763         if (this.type === 'treeview') {
3764             cfg.cls = 'treeview-menu';
3765         }
3766         
3767         return cfg;
3768     },
3769     initEvents : function() {
3770         
3771        // Roo.log("ADD event");
3772        // Roo.log(this.triggerEl.dom);
3773         if (this.triggerEl) {
3774             
3775             this.triggerEl.on('click', this.onTriggerClick, this);
3776             
3777             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3778             
3779             if (!this.hideTrigger) {
3780                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3781                     // dropdown toggle on the 'a' in BS4?
3782                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3783                 } else {
3784                     this.triggerEl.addClass('dropdown-toggle');
3785                 }
3786             }
3787         }
3788         
3789         if (Roo.isTouch) {
3790             this.el.on('touchstart'  , this.onTouch, this);
3791         }
3792         this.el.on('click' , this.onClick, this);
3793
3794         this.el.on("mouseover", this.onMouseOver, this);
3795         this.el.on("mouseout", this.onMouseOut, this);
3796         
3797     },
3798     
3799     findTargetItem : function(e)
3800     {
3801         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3802         if(!t){
3803             return false;
3804         }
3805         //Roo.log(t);         Roo.log(t.id);
3806         if(t && t.id){
3807             //Roo.log(this.menuitems);
3808             return this.menuitems.get(t.id);
3809             
3810             //return this.items.get(t.menuItemId);
3811         }
3812         
3813         return false;
3814     },
3815     
3816     onTouch : function(e) 
3817     {
3818         Roo.log("menu.onTouch");
3819         //e.stopEvent(); this make the user popdown broken
3820         this.onClick(e);
3821     },
3822     
3823     onClick : function(e)
3824     {
3825         Roo.log("menu.onClick");
3826         
3827         var t = this.findTargetItem(e);
3828         if(!t || t.isContainer){
3829             return;
3830         }
3831         Roo.log(e);
3832         /*
3833         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3834             if(t == this.activeItem && t.shouldDeactivate(e)){
3835                 this.activeItem.deactivate();
3836                 delete this.activeItem;
3837                 return;
3838             }
3839             if(t.canActivate){
3840                 this.setActiveItem(t, true);
3841             }
3842             return;
3843             
3844             
3845         }
3846         */
3847        
3848         Roo.log('pass click event');
3849         
3850         t.onClick(e);
3851         
3852         this.fireEvent("click", this, t, e);
3853         
3854         var _this = this;
3855         
3856         if(!t.href.length || t.href == '#'){
3857             (function() { _this.hide(); }).defer(100);
3858         }
3859         
3860     },
3861     
3862     onMouseOver : function(e){
3863         var t  = this.findTargetItem(e);
3864         //Roo.log(t);
3865         //if(t){
3866         //    if(t.canActivate && !t.disabled){
3867         //        this.setActiveItem(t, true);
3868         //    }
3869         //}
3870         
3871         this.fireEvent("mouseover", this, e, t);
3872     },
3873     isVisible : function(){
3874         return !this.hidden;
3875     },
3876     onMouseOut : function(e){
3877         var t  = this.findTargetItem(e);
3878         
3879         //if(t ){
3880         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3881         //        this.activeItem.deactivate();
3882         //        delete this.activeItem;
3883         //    }
3884         //}
3885         this.fireEvent("mouseout", this, e, t);
3886     },
3887     
3888     
3889     /**
3890      * Displays this menu relative to another element
3891      * @param {String/HTMLElement/Roo.Element} element The element to align to
3892      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3893      * the element (defaults to this.defaultAlign)
3894      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3895      */
3896     show : function(el, pos, parentMenu)
3897     {
3898         if (false === this.fireEvent("beforeshow", this)) {
3899             Roo.log("show canceled");
3900             return;
3901         }
3902         this.parentMenu = parentMenu;
3903         if(!this.el){
3904             this.render();
3905         }
3906         this.el.addClass('show'); // show otherwise we do not know how big we are..
3907          
3908         var xy = this.el.getAlignToXY(el, pos);
3909         
3910         // bl-tl << left align  below
3911         // tl-bl << left align 
3912         
3913         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3914             // if it goes to far to the right.. -> align left.
3915             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3916         }
3917         if(xy[0] < 0){
3918             // was left align - go right?
3919             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3920         }
3921         
3922         // goes down the bottom
3923         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3924            xy[1]  < 0 ){
3925             var a = this.align.replace('?', '').split('-');
3926             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3927             
3928         }
3929         
3930         this.showAt(  xy , parentMenu, false);
3931     },
3932      /**
3933      * Displays this menu at a specific xy position
3934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3936      */
3937     showAt : function(xy, parentMenu, /* private: */_e){
3938         this.parentMenu = parentMenu;
3939         if(!this.el){
3940             this.render();
3941         }
3942         if(_e !== false){
3943             this.fireEvent("beforeshow", this);
3944             //xy = this.el.adjustForConstraints(xy);
3945         }
3946         
3947         //this.el.show();
3948         this.hideMenuItems();
3949         this.hidden = false;
3950         if (this.triggerEl) {
3951             this.triggerEl.addClass('open');
3952         }
3953         
3954         this.el.addClass('show');
3955         
3956         
3957         
3958         // reassign x when hitting right
3959         
3960         // reassign y when hitting bottom
3961         
3962         // but the list may align on trigger left or trigger top... should it be a properity?
3963         
3964         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3965             this.el.setXY(xy);
3966         }
3967         
3968         this.focus();
3969         this.fireEvent("show", this);
3970     },
3971     
3972     focus : function(){
3973         return;
3974         if(!this.hidden){
3975             this.doFocus.defer(50, this);
3976         }
3977     },
3978
3979     doFocus : function(){
3980         if(!this.hidden){
3981             this.focusEl.focus();
3982         }
3983     },
3984
3985     /**
3986      * Hides this menu and optionally all parent menus
3987      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3988      */
3989     hide : function(deep)
3990     {
3991         if (false === this.fireEvent("beforehide", this)) {
3992             Roo.log("hide canceled");
3993             return;
3994         }
3995         this.hideMenuItems();
3996         if(this.el && this.isVisible()){
3997            
3998             if(this.activeItem){
3999                 this.activeItem.deactivate();
4000                 this.activeItem = null;
4001             }
4002             if (this.triggerEl) {
4003                 this.triggerEl.removeClass('open');
4004             }
4005             
4006             this.el.removeClass('show');
4007             this.hidden = true;
4008             this.fireEvent("hide", this);
4009         }
4010         if(deep === true && this.parentMenu){
4011             this.parentMenu.hide(true);
4012         }
4013     },
4014     
4015     onTriggerClick : function(e)
4016     {
4017         Roo.log('trigger click');
4018         
4019         var target = e.getTarget();
4020         
4021         Roo.log(target.nodeName.toLowerCase());
4022         
4023         if(target.nodeName.toLowerCase() === 'i'){
4024             e.preventDefault();
4025         }
4026         
4027     },
4028     
4029     onTriggerPress  : function(e)
4030     {
4031         Roo.log('trigger press');
4032         //Roo.log(e.getTarget());
4033        // Roo.log(this.triggerEl.dom);
4034        
4035         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4036         var pel = Roo.get(e.getTarget());
4037         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4038             Roo.log('is treeview or dropdown?');
4039             return;
4040         }
4041         
4042         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4043             return;
4044         }
4045         
4046         if (this.isVisible()) {
4047             Roo.log('hide');
4048             this.hide();
4049         } else {
4050             Roo.log('show');
4051             
4052             this.show(this.triggerEl, this.align, false);
4053         }
4054         
4055         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4056             e.stopEvent();
4057         }
4058         
4059     },
4060        
4061     
4062     hideMenuItems : function()
4063     {
4064         Roo.log("hide Menu Items");
4065         if (!this.el) { 
4066             return;
4067         }
4068         
4069         this.el.select('.open',true).each(function(aa) {
4070             
4071             aa.removeClass('open');
4072          
4073         });
4074     },
4075     addxtypeChild : function (tree, cntr) {
4076         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4077           
4078         this.menuitems.add(comp);
4079         return comp;
4080
4081     },
4082     getEl : function()
4083     {
4084         Roo.log(this.el);
4085         return this.el;
4086     },
4087     
4088     clear : function()
4089     {
4090         this.getEl().dom.innerHTML = '';
4091         this.menuitems.clear();
4092     }
4093 });
4094
4095  
4096  /**
4097  * @class Roo.bootstrap.menu.Item
4098  * @extends Roo.bootstrap.Component
4099  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4100  * @parent Roo.bootstrap.menu.Menu
4101  * @licence LGPL
4102  * Bootstrap MenuItem class
4103  * 
4104  * @cfg {String} html the menu label
4105  * @cfg {String} href the link
4106  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4107  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4108  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4109  * @cfg {String} fa favicon to show on left of menu item.
4110  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4111  * 
4112  * 
4113  * @constructor
4114  * Create a new MenuItem
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.menu.Item = function(config){
4120     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4121     this.addEvents({
4122         // raw events
4123         /**
4124          * @event click
4125          * The raw click event for the entire grid.
4126          * @param {Roo.bootstrap.menu.Item} this
4127          * @param {Roo.EventObject} e
4128          */
4129         "click" : true
4130     });
4131 };
4132
4133 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4134     
4135     href : false,
4136     html : false,
4137     preventDefault: false,
4138     isContainer : false,
4139     active : false,
4140     fa: false,
4141     
4142     getAutoCreate : function(){
4143         
4144         if(this.isContainer){
4145             return {
4146                 tag: 'li',
4147                 cls: 'dropdown-menu-item '
4148             };
4149         }
4150         var ctag = {
4151             tag: 'span',
4152             html: 'Link'
4153         };
4154         
4155         var anc = {
4156             tag : 'a',
4157             cls : 'dropdown-item',
4158             href : '#',
4159             cn : [  ]
4160         };
4161         
4162         if (this.fa !== false) {
4163             anc.cn.push({
4164                 tag : 'i',
4165                 cls : 'fa fa-' + this.fa
4166             });
4167         }
4168         
4169         anc.cn.push(ctag);
4170         
4171         
4172         var cfg= {
4173             tag: 'li',
4174             cls: 'dropdown-menu-item',
4175             cn: [ anc ]
4176         };
4177         if (this.parent().type == 'treeview') {
4178             cfg.cls = 'treeview-menu';
4179         }
4180         if (this.active) {
4181             cfg.cls += ' active';
4182         }
4183         
4184         
4185         
4186         anc.href = this.href || cfg.cn[0].href ;
4187         ctag.html = this.html || cfg.cn[0].html ;
4188         return cfg;
4189     },
4190     
4191     initEvents: function()
4192     {
4193         if (this.parent().type == 'treeview') {
4194             this.el.select('a').on('click', this.onClick, this);
4195         }
4196         
4197         if (this.menu) {
4198             this.menu.parentType = this.xtype;
4199             this.menu.triggerEl = this.el;
4200             this.menu = this.addxtype(Roo.apply({}, this.menu));
4201         }
4202         
4203     },
4204     onClick : function(e)
4205     {
4206         //Roo.log('item on click ');
4207         
4208         if(this.href === false || this.preventDefault){
4209             e.preventDefault();
4210         }
4211         //this.parent().hideMenuItems();
4212         
4213         this.fireEvent('click', this, e);
4214     },
4215     getEl : function()
4216     {
4217         return this.el;
4218     } 
4219 });
4220
4221  
4222
4223  
4224
4225   
4226 /**
4227  * @class Roo.bootstrap.menu.Separator
4228  * @extends Roo.bootstrap.Component
4229  * @licence LGPL
4230  * @parent Roo.bootstrap.menu.Menu
4231  * Bootstrap Separator class
4232  * 
4233  * @constructor
4234  * Create a new Separator
4235  * @param {Object} config The config object
4236  */
4237
4238
4239 Roo.bootstrap.menu.Separator = function(config){
4240     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4241 };
4242
4243 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4244     
4245     getAutoCreate : function(){
4246         var cfg = {
4247             tag : 'li',
4248             cls: 'dropdown-divider divider'
4249         };
4250         
4251         return cfg;
4252     }
4253    
4254 });
4255
4256  
4257
4258  
4259 /*
4260 * Licence: LGPL
4261 */
4262
4263 /**
4264  * @class Roo.bootstrap.Modal
4265  * @extends Roo.bootstrap.Component
4266  * @parent none builder
4267  * @children Roo.bootstrap.Component
4268  * Bootstrap Modal class
4269  * @cfg {String} title Title of dialog
4270  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4271  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4272  * @cfg {Boolean} specificTitle default false
4273  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4274  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4275  * @cfg {Boolean} animate default true
4276  * @cfg {Boolean} allow_close default true
4277  * @cfg {Boolean} fitwindow default false
4278  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4279  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4280  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4281  * @cfg {String} size (sm|lg|xl) default empty
4282  * @cfg {Number} max_width set the max width of modal
4283  * @cfg {Boolean} editableTitle can the title be edited
4284
4285  *
4286  *
4287  * @constructor
4288  * Create a new Modal Dialog
4289  * @param {Object} config The config object
4290  */
4291
4292 Roo.bootstrap.Modal = function(config){
4293     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4294     this.addEvents({
4295         // raw events
4296         /**
4297          * @event btnclick
4298          * The raw btnclick event for the button
4299          * @param {Roo.EventObject} e
4300          */
4301         "btnclick" : true,
4302         /**
4303          * @event resize
4304          * Fire when dialog resize
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} e
4307          */
4308         "resize" : true,
4309         /**
4310          * @event titlechanged
4311          * Fire when the editable title has been changed
4312          * @param {Roo.bootstrap.Modal} this
4313          * @param {Roo.EventObject} value
4314          */
4315         "titlechanged" : true 
4316         
4317     });
4318     this.buttons = this.buttons || [];
4319
4320     if (this.tmpl) {
4321         this.tmpl = Roo.factory(this.tmpl);
4322     }
4323
4324 };
4325
4326 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4327
4328     title : 'test dialog',
4329
4330     buttons : false,
4331
4332     // set on load...
4333
4334     html: false,
4335
4336     tmp: false,
4337
4338     specificTitle: false,
4339
4340     buttonPosition: 'right',
4341
4342     allow_close : true,
4343
4344     animate : true,
4345
4346     fitwindow: false,
4347     
4348      // private
4349     dialogEl: false,
4350     bodyEl:  false,
4351     footerEl:  false,
4352     titleEl:  false,
4353     closeEl:  false,
4354
4355     size: '',
4356     
4357     max_width: 0,
4358     
4359     max_height: 0,
4360     
4361     fit_content: false,
4362     editableTitle  : false,
4363
4364     onRender : function(ct, position)
4365     {
4366         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4367
4368         if(!this.el){
4369             var cfg = Roo.apply({},  this.getAutoCreate());
4370             cfg.id = Roo.id();
4371             //if(!cfg.name){
4372             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4373             //}
4374             //if (!cfg.name.length) {
4375             //    delete cfg.name;
4376            // }
4377             if (this.cls) {
4378                 cfg.cls += ' ' + this.cls;
4379             }
4380             if (this.style) {
4381                 cfg.style = this.style;
4382             }
4383             this.el = Roo.get(document.body).createChild(cfg, position);
4384         }
4385         //var type = this.el.dom.type;
4386
4387
4388         if(this.tabIndex !== undefined){
4389             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4390         }
4391
4392         this.dialogEl = this.el.select('.modal-dialog',true).first();
4393         this.bodyEl = this.el.select('.modal-body',true).first();
4394         this.closeEl = this.el.select('.modal-header .close', true).first();
4395         this.headerEl = this.el.select('.modal-header',true).first();
4396         this.titleEl = this.el.select('.modal-title',true).first();
4397         this.footerEl = this.el.select('.modal-footer',true).first();
4398
4399         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4400         
4401         //this.el.addClass("x-dlg-modal");
4402
4403         if (this.buttons.length) {
4404             Roo.each(this.buttons, function(bb) {
4405                 var b = Roo.apply({}, bb);
4406                 b.xns = b.xns || Roo.bootstrap;
4407                 b.xtype = b.xtype || 'Button';
4408                 if (typeof(b.listeners) == 'undefined') {
4409                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4410                 }
4411
4412                 var btn = Roo.factory(b);
4413
4414                 btn.render(this.getButtonContainer());
4415
4416             },this);
4417         }
4418         // render the children.
4419         var nitems = [];
4420
4421         if(typeof(this.items) != 'undefined'){
4422             var items = this.items;
4423             delete this.items;
4424
4425             for(var i =0;i < items.length;i++) {
4426                 // we force children not to montor widnow resize  - as we do that for them.
4427                 items[i].monitorWindowResize = false;
4428                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4429             }
4430         }
4431
4432         this.items = nitems;
4433
4434         // where are these used - they used to be body/close/footer
4435
4436
4437         this.initEvents();
4438         //this.el.addClass([this.fieldClass, this.cls]);
4439
4440     },
4441
4442     getAutoCreate : function()
4443     {
4444         // we will default to modal-body-overflow - might need to remove or make optional later.
4445         var bdy = {
4446                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4447                 html : this.html || ''
4448         };
4449
4450         var title = {
4451             tag: 'h5',
4452             cls : 'modal-title',
4453             html : this.title
4454         };
4455
4456         if(this.specificTitle){ // WTF is this?
4457             title = this.title;
4458         }
4459
4460         var header = [];
4461         if (this.allow_close && Roo.bootstrap.version == 3) {
4462             header.push({
4463                 tag: 'button',
4464                 cls : 'close',
4465                 html : '&times'
4466             });
4467         }
4468
4469         header.push(title);
4470
4471         if (this.editableTitle) {
4472             header.push({
4473                 cls: 'form-control roo-editable-title d-none',
4474                 tag: 'input',
4475                 type: 'text'
4476             });
4477         }
4478         
4479         if (this.allow_close && Roo.bootstrap.version == 4) {
4480             header.push({
4481                 tag: 'button',
4482                 cls : 'close',
4483                 html : '&times'
4484             });
4485         }
4486         
4487         var size = '';
4488
4489         if(this.size.length){
4490             size = 'modal-' + this.size;
4491         }
4492         
4493         var footer = Roo.bootstrap.version == 3 ?
4494             {
4495                 cls : 'modal-footer',
4496                 cn : [
4497                     {
4498                         tag: 'div',
4499                         cls: 'btn-' + this.buttonPosition
4500                     }
4501                 ]
4502
4503             } :
4504             {  // BS4 uses mr-auto on left buttons....
4505                 cls : 'modal-footer'
4506             };
4507
4508             
4509
4510         
4511         
4512         var modal = {
4513             cls: "modal",
4514              cn : [
4515                 {
4516                     cls: "modal-dialog " + size,
4517                     cn : [
4518                         {
4519                             cls : "modal-content",
4520                             cn : [
4521                                 {
4522                                     cls : 'modal-header',
4523                                     cn : header
4524                                 },
4525                                 bdy,
4526                                 footer
4527                             ]
4528
4529                         }
4530                     ]
4531
4532                 }
4533             ]
4534         };
4535
4536         if(this.animate){
4537             modal.cls += ' fade';
4538         }
4539
4540         return modal;
4541
4542     },
4543     getChildContainer : function() {
4544
4545          return this.bodyEl;
4546
4547     },
4548     getButtonContainer : function() {
4549         
4550          return Roo.bootstrap.version == 4 ?
4551             this.el.select('.modal-footer',true).first()
4552             : this.el.select('.modal-footer div',true).first();
4553
4554     },
4555     
4556     closeClick : function()
4557     {
4558         this.hide();
4559     },
4560     
4561     initEvents : function()
4562     {
4563         if (this.allow_close) {
4564             this.closeEl.on('click', this.closeClick, this);
4565         }
4566         Roo.EventManager.onWindowResize(this.resize, this, true);
4567         if (this.editableTitle) {
4568             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4569             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4570             this.headerEditEl.on('keyup', function(e) {
4571                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4572                         this.toggleHeaderInput(false)
4573                     }
4574                 }, this);
4575             this.headerEditEl.on('blur', function(e) {
4576                 this.toggleHeaderInput(false)
4577             },this);
4578         }
4579
4580     },
4581   
4582
4583     resize : function()
4584     {
4585         this.maskEl.setSize(
4586             Roo.lib.Dom.getViewWidth(true),
4587             Roo.lib.Dom.getViewHeight(true)
4588         );
4589         
4590         if (this.fitwindow) {
4591             
4592            this.dialogEl.setStyle( { 'max-width' : '100%' });
4593             this.setSize(
4594                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4595                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4596             );
4597             return;
4598         }
4599         
4600         if(this.max_width !== 0) {
4601             
4602             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4603             
4604             if(this.height) {
4605                 this.setSize(w, this.height);
4606                 return;
4607             }
4608             
4609             if(this.max_height) {
4610                 this.setSize(w,Math.min(
4611                     this.max_height,
4612                     Roo.lib.Dom.getViewportHeight(true) - 60
4613                 ));
4614                 
4615                 return;
4616             }
4617             
4618             if(!this.fit_content) {
4619                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4620                 return;
4621             }
4622             
4623             this.setSize(w, Math.min(
4624                 60 +
4625                 this.headerEl.getHeight() + 
4626                 this.footerEl.getHeight() + 
4627                 this.getChildHeight(this.bodyEl.dom.childNodes),
4628                 Roo.lib.Dom.getViewportHeight(true) - 60)
4629             );
4630         }
4631         
4632     },
4633
4634     setSize : function(w,h)
4635     {
4636         if (!w && !h) {
4637             return;
4638         }
4639         
4640         this.resizeTo(w,h);
4641         // any layout/border etc.. resize..
4642         (function () {
4643             this.items.forEach( function(e) {
4644                 e.layout ? e.layout() : false;
4645
4646             });
4647         }).defer(100,this);
4648         
4649     },
4650
4651     show : function() {
4652
4653         if (!this.rendered) {
4654             this.render();
4655         }
4656         this.toggleHeaderInput(false);
4657         //this.el.setStyle('display', 'block');
4658         this.el.removeClass('hideing');
4659         this.el.dom.style.display='block';
4660         
4661         Roo.get(document.body).addClass('modal-open');
4662  
4663         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4664             
4665             (function(){
4666                 this.el.addClass('show');
4667                 this.el.addClass('in');
4668             }).defer(50, this);
4669         }else{
4670             this.el.addClass('show');
4671             this.el.addClass('in');
4672         }
4673
4674         // not sure how we can show data in here..
4675         //if (this.tmpl) {
4676         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4677         //}
4678
4679         Roo.get(document.body).addClass("x-body-masked");
4680         
4681         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4682         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4683         this.maskEl.dom.style.display = 'block';
4684         this.maskEl.addClass('show');
4685         
4686         
4687         this.resize();
4688         
4689         this.fireEvent('show', this);
4690
4691         // set zindex here - otherwise it appears to be ignored...
4692         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4693         
4694         
4695         // this is for children that are... layout.Border 
4696         (function () {
4697             this.items.forEach( function(e) {
4698                 e.layout ? e.layout() : false;
4699
4700             });
4701         }).defer(100,this);
4702
4703     },
4704     hide : function()
4705     {
4706         if(this.fireEvent("beforehide", this) !== false){
4707             
4708             this.maskEl.removeClass('show');
4709             
4710             this.maskEl.dom.style.display = '';
4711             Roo.get(document.body).removeClass("x-body-masked");
4712             this.el.removeClass('in');
4713             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4714
4715             if(this.animate){ // why
4716                 this.el.addClass('hideing');
4717                 this.el.removeClass('show');
4718                 (function(){
4719                     if (!this.el.hasClass('hideing')) {
4720                         return; // it's been shown again...
4721                     }
4722                     
4723                     this.el.dom.style.display='';
4724
4725                     Roo.get(document.body).removeClass('modal-open');
4726                     this.el.removeClass('hideing');
4727                 }).defer(150,this);
4728                 
4729             }else{
4730                 this.el.removeClass('show');
4731                 this.el.dom.style.display='';
4732                 Roo.get(document.body).removeClass('modal-open');
4733
4734             }
4735             this.fireEvent('hide', this);
4736         }
4737     },
4738     isVisible : function()
4739     {
4740         
4741         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4742         
4743     },
4744
4745     addButton : function(str, cb)
4746     {
4747
4748
4749         var b = Roo.apply({}, { html : str } );
4750         b.xns = b.xns || Roo.bootstrap;
4751         b.xtype = b.xtype || 'Button';
4752         if (typeof(b.listeners) == 'undefined') {
4753             b.listeners = { click : cb.createDelegate(this)  };
4754         }
4755
4756         var btn = Roo.factory(b);
4757
4758         btn.render(this.getButtonContainer());
4759
4760         return btn;
4761
4762     },
4763
4764     setDefaultButton : function(btn)
4765     {
4766         //this.el.select('.modal-footer').()
4767     },
4768
4769     resizeTo: function(w,h)
4770     {
4771         this.dialogEl.setWidth(w);
4772         
4773         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4774
4775         this.bodyEl.setHeight(h - diff);
4776         
4777         this.fireEvent('resize', this);
4778     },
4779     
4780     setContentSize  : function(w, h)
4781     {
4782
4783     },
4784     onButtonClick: function(btn,e)
4785     {
4786         //Roo.log([a,b,c]);
4787         this.fireEvent('btnclick', btn.name, e);
4788     },
4789      /**
4790      * Set the title of the Dialog
4791      * @param {String} str new Title
4792      */
4793     setTitle: function(str) {
4794         this.titleEl.dom.innerHTML = str;
4795         this.title = str;
4796     },
4797     /**
4798      * Set the body of the Dialog
4799      * @param {String} str new Title
4800      */
4801     setBody: function(str) {
4802         this.bodyEl.dom.innerHTML = str;
4803     },
4804     /**
4805      * Set the body of the Dialog using the template
4806      * @param {Obj} data - apply this data to the template and replace the body contents.
4807      */
4808     applyBody: function(obj)
4809     {
4810         if (!this.tmpl) {
4811             Roo.log("Error - using apply Body without a template");
4812             //code
4813         }
4814         this.tmpl.overwrite(this.bodyEl, obj);
4815     },
4816     
4817     getChildHeight : function(child_nodes)
4818     {
4819         if(
4820             !child_nodes ||
4821             child_nodes.length == 0
4822         ) {
4823             return 0;
4824         }
4825         
4826         var child_height = 0;
4827         
4828         for(var i = 0; i < child_nodes.length; i++) {
4829             
4830             /*
4831             * for modal with tabs...
4832             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4833                 
4834                 var layout_childs = child_nodes[i].childNodes;
4835                 
4836                 for(var j = 0; j < layout_childs.length; j++) {
4837                     
4838                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4839                         
4840                         var layout_body_childs = layout_childs[j].childNodes;
4841                         
4842                         for(var k = 0; k < layout_body_childs.length; k++) {
4843                             
4844                             if(layout_body_childs[k].classList.contains('navbar')) {
4845                                 child_height += layout_body_childs[k].offsetHeight;
4846                                 continue;
4847                             }
4848                             
4849                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4850                                 
4851                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4852                                 
4853                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4854                                     
4855                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4856                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4857                                         continue;
4858                                     }
4859                                     
4860                                 }
4861                                 
4862                             }
4863                             
4864                         }
4865                     }
4866                 }
4867                 continue;
4868             }
4869             */
4870             
4871             child_height += child_nodes[i].offsetHeight;
4872             // Roo.log(child_nodes[i].offsetHeight);
4873         }
4874         
4875         return child_height;
4876     },
4877     toggleHeaderInput : function(is_edit)
4878     {
4879         if (!this.editableTitle) {
4880             return; // not editable.
4881         }
4882         if (is_edit && this.is_header_editing) {
4883             return; // already editing..
4884         }
4885         if (is_edit) {
4886     
4887             this.headerEditEl.dom.value = this.title;
4888             this.headerEditEl.removeClass('d-none');
4889             this.headerEditEl.dom.focus();
4890             this.titleEl.addClass('d-none');
4891             
4892             this.is_header_editing = true;
4893             return
4894         }
4895         // flip back to not editing.
4896         this.title = this.headerEditEl.dom.value;
4897         this.headerEditEl.addClass('d-none');
4898         this.titleEl.removeClass('d-none');
4899         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4900         this.is_header_editing = false;
4901         this.fireEvent('titlechanged', this, this.title);
4902     
4903             
4904         
4905     }
4906
4907 });
4908
4909
4910 Roo.apply(Roo.bootstrap.Modal,  {
4911     /**
4912          * Button config that displays a single OK button
4913          * @type Object
4914          */
4915         OK :  [{
4916             name : 'ok',
4917             weight : 'primary',
4918             html : 'OK'
4919         }],
4920         /**
4921          * Button config that displays Yes and No buttons
4922          * @type Object
4923          */
4924         YESNO : [
4925             {
4926                 name  : 'no',
4927                 html : 'No'
4928             },
4929             {
4930                 name  :'yes',
4931                 weight : 'primary',
4932                 html : 'Yes'
4933             }
4934         ],
4935
4936         /**
4937          * Button config that displays OK and Cancel buttons
4938          * @type Object
4939          */
4940         OKCANCEL : [
4941             {
4942                name : 'cancel',
4943                 html : 'Cancel'
4944             },
4945             {
4946                 name : 'ok',
4947                 weight : 'primary',
4948                 html : 'OK'
4949             }
4950         ],
4951         /**
4952          * Button config that displays Yes, No and Cancel buttons
4953          * @type Object
4954          */
4955         YESNOCANCEL : [
4956             {
4957                 name : 'yes',
4958                 weight : 'primary',
4959                 html : 'Yes'
4960             },
4961             {
4962                 name : 'no',
4963                 html : 'No'
4964             },
4965             {
4966                 name : 'cancel',
4967                 html : 'Cancel'
4968             }
4969         ],
4970         
4971         zIndex : 10001
4972 });
4973
4974 /*
4975  * - LGPL
4976  *
4977  * messagebox - can be used as a replace
4978  * 
4979  */
4980 /**
4981  * @class Roo.MessageBox
4982  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4983  * Example usage:
4984  *<pre><code>
4985 // Basic alert:
4986 Roo.Msg.alert('Status', 'Changes saved successfully.');
4987
4988 // Prompt for user data:
4989 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4990     if (btn == 'ok'){
4991         // process text value...
4992     }
4993 });
4994
4995 // Show a dialog using config options:
4996 Roo.Msg.show({
4997    title:'Save Changes?',
4998    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4999    buttons: Roo.Msg.YESNOCANCEL,
5000    fn: processResult,
5001    animEl: 'elId'
5002 });
5003 </code></pre>
5004  * @static
5005  */
5006 Roo.bootstrap.MessageBox = function(){
5007     var dlg, opt, mask, waitTimer;
5008     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5009     var buttons, activeTextEl, bwidth;
5010
5011     
5012     // private
5013     var handleButton = function(button){
5014         dlg.hide();
5015         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5016     };
5017
5018     // private
5019     var handleHide = function(){
5020         if(opt && opt.cls){
5021             dlg.el.removeClass(opt.cls);
5022         }
5023         //if(waitTimer){
5024         //    Roo.TaskMgr.stop(waitTimer);
5025         //    waitTimer = null;
5026         //}
5027     };
5028
5029     // private
5030     var updateButtons = function(b){
5031         var width = 0;
5032         if(!b){
5033             buttons["ok"].hide();
5034             buttons["cancel"].hide();
5035             buttons["yes"].hide();
5036             buttons["no"].hide();
5037             dlg.footerEl.hide();
5038             
5039             return width;
5040         }
5041         dlg.footerEl.show();
5042         for(var k in buttons){
5043             if(typeof buttons[k] != "function"){
5044                 if(b[k]){
5045                     buttons[k].show();
5046                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5047                     width += buttons[k].el.getWidth()+15;
5048                 }else{
5049                     buttons[k].hide();
5050                 }
5051             }
5052         }
5053         return width;
5054     };
5055
5056     // private
5057     var handleEsc = function(d, k, e){
5058         if(opt && opt.closable !== false){
5059             dlg.hide();
5060         }
5061         if(e){
5062             e.stopEvent();
5063         }
5064     };
5065
5066     return {
5067         /**
5068          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5069          * @return {Roo.BasicDialog} The BasicDialog element
5070          */
5071         getDialog : function(){
5072            if(!dlg){
5073                 dlg = new Roo.bootstrap.Modal( {
5074                     //draggable: true,
5075                     //resizable:false,
5076                     //constraintoviewport:false,
5077                     //fixedcenter:true,
5078                     //collapsible : false,
5079                     //shim:true,
5080                     //modal: true,
5081                 //    width: 'auto',
5082                   //  height:100,
5083                     //buttonAlign:"center",
5084                     closeClick : function(){
5085                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5086                             handleButton("no");
5087                         }else{
5088                             handleButton("cancel");
5089                         }
5090                     }
5091                 });
5092                 dlg.render();
5093                 dlg.on("hide", handleHide);
5094                 mask = dlg.mask;
5095                 //dlg.addKeyListener(27, handleEsc);
5096                 buttons = {};
5097                 this.buttons = buttons;
5098                 var bt = this.buttonText;
5099                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5100                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5101                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5102                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5103                 //Roo.log(buttons);
5104                 bodyEl = dlg.bodyEl.createChild({
5105
5106                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5107                         '<textarea class="roo-mb-textarea"></textarea>' +
5108                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5109                 });
5110                 msgEl = bodyEl.dom.firstChild;
5111                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5112                 textboxEl.enableDisplayMode();
5113                 textboxEl.addKeyListener([10,13], function(){
5114                     if(dlg.isVisible() && opt && opt.buttons){
5115                         if(opt.buttons.ok){
5116                             handleButton("ok");
5117                         }else if(opt.buttons.yes){
5118                             handleButton("yes");
5119                         }
5120                     }
5121                 });
5122                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5123                 textareaEl.enableDisplayMode();
5124                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5125                 progressEl.enableDisplayMode();
5126                 
5127                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5128                 var pf = progressEl.dom.firstChild;
5129                 if (pf) {
5130                     pp = Roo.get(pf.firstChild);
5131                     pp.setHeight(pf.offsetHeight);
5132                 }
5133                 
5134             }
5135             return dlg;
5136         },
5137
5138         /**
5139          * Updates the message box body text
5140          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5141          * the XHTML-compliant non-breaking space character '&amp;#160;')
5142          * @return {Roo.MessageBox} This message box
5143          */
5144         updateText : function(text)
5145         {
5146             if(!dlg.isVisible() && !opt.width){
5147                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5148                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5149             }
5150             msgEl.innerHTML = text || '&#160;';
5151       
5152             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5153             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5154             var w = Math.max(
5155                     Math.min(opt.width || cw , this.maxWidth), 
5156                     Math.max(opt.minWidth || this.minWidth, bwidth)
5157             );
5158             if(opt.prompt){
5159                 activeTextEl.setWidth(w);
5160             }
5161             if(dlg.isVisible()){
5162                 dlg.fixedcenter = false;
5163             }
5164             // to big, make it scroll. = But as usual stupid IE does not support
5165             // !important..
5166             
5167             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5168                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5169                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5170             } else {
5171                 bodyEl.dom.style.height = '';
5172                 bodyEl.dom.style.overflowY = '';
5173             }
5174             if (cw > w) {
5175                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5176             } else {
5177                 bodyEl.dom.style.overflowX = '';
5178             }
5179             
5180             dlg.setContentSize(w, bodyEl.getHeight());
5181             if(dlg.isVisible()){
5182                 dlg.fixedcenter = true;
5183             }
5184             return this;
5185         },
5186
5187         /**
5188          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5189          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5190          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5191          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5192          * @return {Roo.MessageBox} This message box
5193          */
5194         updateProgress : function(value, text){
5195             if(text){
5196                 this.updateText(text);
5197             }
5198             
5199             if (pp) { // weird bug on my firefox - for some reason this is not defined
5200                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5201                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5202             }
5203             return this;
5204         },        
5205
5206         /**
5207          * Returns true if the message box is currently displayed
5208          * @return {Boolean} True if the message box is visible, else false
5209          */
5210         isVisible : function(){
5211             return dlg && dlg.isVisible();  
5212         },
5213
5214         /**
5215          * Hides the message box if it is displayed
5216          */
5217         hide : function(){
5218             if(this.isVisible()){
5219                 dlg.hide();
5220             }  
5221         },
5222
5223         /**
5224          * Displays a new message box, or reinitializes an existing message box, based on the config options
5225          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5226          * The following config object properties are supported:
5227          * <pre>
5228 Property    Type             Description
5229 ----------  ---------------  ------------------------------------------------------------------------------------
5230 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5231                                    closes (defaults to undefined)
5232 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5233                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5234 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5235                                    progress and wait dialogs will ignore this property and always hide the
5236                                    close button as they can only be closed programmatically.
5237 cls               String           A custom CSS class to apply to the message box element
5238 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5239                                    displayed (defaults to 75)
5240 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5241                                    function will be btn (the name of the button that was clicked, if applicable,
5242                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5243                                    Progress and wait dialogs will ignore this option since they do not respond to
5244                                    user actions and can only be closed programmatically, so any required function
5245                                    should be called by the same code after it closes the dialog.
5246 icon              String           A CSS class that provides a background image to be used as an icon for
5247                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5248 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5249 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5250 modal             Boolean          False to allow user interaction with the page while the message box is
5251                                    displayed (defaults to true)
5252 msg               String           A string that will replace the existing message box body text (defaults
5253                                    to the XHTML-compliant non-breaking space character '&#160;')
5254 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5255 progress          Boolean          True to display a progress bar (defaults to false)
5256 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5257 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5258 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5259 title             String           The title text
5260 value             String           The string value to set into the active textbox element if displayed
5261 wait              Boolean          True to display a progress bar (defaults to false)
5262 width             Number           The width of the dialog in pixels
5263 </pre>
5264          *
5265          * Example usage:
5266          * <pre><code>
5267 Roo.Msg.show({
5268    title: 'Address',
5269    msg: 'Please enter your address:',
5270    width: 300,
5271    buttons: Roo.MessageBox.OKCANCEL,
5272    multiline: true,
5273    fn: saveAddress,
5274    animEl: 'addAddressBtn'
5275 });
5276 </code></pre>
5277          * @param {Object} config Configuration options
5278          * @return {Roo.MessageBox} This message box
5279          */
5280         show : function(options)
5281         {
5282             
5283             // this causes nightmares if you show one dialog after another
5284             // especially on callbacks..
5285              
5286             if(this.isVisible()){
5287                 
5288                 this.hide();
5289                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5290                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5291                 Roo.log("New Dialog Message:" +  options.msg )
5292                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5293                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5294                 
5295             }
5296             var d = this.getDialog();
5297             opt = options;
5298             d.setTitle(opt.title || "&#160;");
5299             d.closeEl.setDisplayed(opt.closable !== false);
5300             activeTextEl = textboxEl;
5301             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5302             if(opt.prompt){
5303                 if(opt.multiline){
5304                     textboxEl.hide();
5305                     textareaEl.show();
5306                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5307                         opt.multiline : this.defaultTextHeight);
5308                     activeTextEl = textareaEl;
5309                 }else{
5310                     textboxEl.show();
5311                     textareaEl.hide();
5312                 }
5313             }else{
5314                 textboxEl.hide();
5315                 textareaEl.hide();
5316             }
5317             progressEl.setDisplayed(opt.progress === true);
5318             if (opt.progress) {
5319                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5320             }
5321             this.updateProgress(0);
5322             activeTextEl.dom.value = opt.value || "";
5323             if(opt.prompt){
5324                 dlg.setDefaultButton(activeTextEl);
5325             }else{
5326                 var bs = opt.buttons;
5327                 var db = null;
5328                 if(bs && bs.ok){
5329                     db = buttons["ok"];
5330                 }else if(bs && bs.yes){
5331                     db = buttons["yes"];
5332                 }
5333                 dlg.setDefaultButton(db);
5334             }
5335             bwidth = updateButtons(opt.buttons);
5336             this.updateText(opt.msg);
5337             if(opt.cls){
5338                 d.el.addClass(opt.cls);
5339             }
5340             d.proxyDrag = opt.proxyDrag === true;
5341             d.modal = opt.modal !== false;
5342             d.mask = opt.modal !== false ? mask : false;
5343             if(!d.isVisible()){
5344                 // force it to the end of the z-index stack so it gets a cursor in FF
5345                 document.body.appendChild(dlg.el.dom);
5346                 d.animateTarget = null;
5347                 d.show(options.animEl);
5348             }
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5354          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5355          * and closing the message box when the process is complete.
5356          * @param {String} title The title bar text
5357          * @param {String} msg The message box body text
5358          * @return {Roo.MessageBox} This message box
5359          */
5360         progress : function(title, msg){
5361             this.show({
5362                 title : title,
5363                 msg : msg,
5364                 buttons: false,
5365                 progress:true,
5366                 closable:false,
5367                 minWidth: this.minProgressWidth,
5368                 modal : true
5369             });
5370             return this;
5371         },
5372
5373         /**
5374          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5375          * If a callback function is passed it will be called after the user clicks the button, and the
5376          * id of the button that was clicked will be passed as the only parameter to the callback
5377          * (could also be the top-right close button).
5378          * @param {String} title The title bar text
5379          * @param {String} msg The message box body text
5380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5381          * @param {Object} scope (optional) The scope of the callback function
5382          * @return {Roo.MessageBox} This message box
5383          */
5384         alert : function(title, msg, fn, scope)
5385         {
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: this.OK,
5390                 fn: fn,
5391                 closable : false,
5392                 scope : scope,
5393                 modal : true
5394             });
5395             return this;
5396         },
5397
5398         /**
5399          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5400          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5401          * You are responsible for closing the message box when the process is complete.
5402          * @param {String} msg The message box body text
5403          * @param {String} title (optional) The title bar text
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         wait : function(msg, title){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: false,
5411                 closable:false,
5412                 progress:true,
5413                 modal:true,
5414                 width:300,
5415                 wait:true
5416             });
5417             waitTimer = Roo.TaskMgr.start({
5418                 run: function(i){
5419                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5420                 },
5421                 interval: 1000
5422             });
5423             return this;
5424         },
5425
5426         /**
5427          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5428          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5429          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5430          * @param {String} title The title bar text
5431          * @param {String} msg The message box body text
5432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5433          * @param {Object} scope (optional) The scope of the callback function
5434          * @return {Roo.MessageBox} This message box
5435          */
5436         confirm : function(title, msg, fn, scope){
5437             this.show({
5438                 title : title,
5439                 msg : msg,
5440                 buttons: this.YESNO,
5441                 fn: fn,
5442                 scope : scope,
5443                 modal : true
5444             });
5445             return this;
5446         },
5447
5448         /**
5449          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5450          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5451          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5452          * (could also be the top-right close button) and the text that was entered will be passed as the two
5453          * parameters to the callback.
5454          * @param {String} title The title bar text
5455          * @param {String} msg The message box body text
5456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5457          * @param {Object} scope (optional) The scope of the callback function
5458          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5459          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5460          * @return {Roo.MessageBox} This message box
5461          */
5462         prompt : function(title, msg, fn, scope, multiline){
5463             this.show({
5464                 title : title,
5465                 msg : msg,
5466                 buttons: this.OKCANCEL,
5467                 fn: fn,
5468                 minWidth:250,
5469                 scope : scope,
5470                 prompt:true,
5471                 multiline: multiline,
5472                 modal : true
5473             });
5474             return this;
5475         },
5476
5477         /**
5478          * Button config that displays a single OK button
5479          * @type Object
5480          */
5481         OK : {ok:true},
5482         /**
5483          * Button config that displays Yes and No buttons
5484          * @type Object
5485          */
5486         YESNO : {yes:true, no:true},
5487         /**
5488          * Button config that displays OK and Cancel buttons
5489          * @type Object
5490          */
5491         OKCANCEL : {ok:true, cancel:true},
5492         /**
5493          * Button config that displays Yes, No and Cancel buttons
5494          * @type Object
5495          */
5496         YESNOCANCEL : {yes:true, no:true, cancel:true},
5497
5498         /**
5499          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5500          * @type Number
5501          */
5502         defaultTextHeight : 75,
5503         /**
5504          * The maximum width in pixels of the message box (defaults to 600)
5505          * @type Number
5506          */
5507         maxWidth : 600,
5508         /**
5509          * The minimum width in pixels of the message box (defaults to 100)
5510          * @type Number
5511          */
5512         minWidth : 100,
5513         /**
5514          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5515          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5516          * @type Number
5517          */
5518         minProgressWidth : 250,
5519         /**
5520          * An object containing the default button text strings that can be overriden for localized language support.
5521          * Supported properties are: ok, cancel, yes and no.
5522          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5523          * @type Object
5524          */
5525         buttonText : {
5526             ok : "OK",
5527             cancel : "Cancel",
5528             yes : "Yes",
5529             no : "No"
5530         }
5531     };
5532 }();
5533
5534 /**
5535  * Shorthand for {@link Roo.MessageBox}
5536  */
5537 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5538 Roo.Msg = Roo.Msg || Roo.MessageBox;
5539 /*
5540  * - LGPL
5541  *
5542  * navbar
5543  * 
5544  */
5545
5546 /**
5547  * @class Roo.bootstrap.nav.Bar
5548  * @extends Roo.bootstrap.Component
5549  * @abstract
5550  * Bootstrap Navbar class
5551
5552  * @constructor
5553  * Create a new Navbar
5554  * @param {Object} config The config object
5555  */
5556
5557
5558 Roo.bootstrap.nav.Bar = function(config){
5559     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5560     this.addEvents({
5561         // raw events
5562         /**
5563          * @event beforetoggle
5564          * Fire before toggle the menu
5565          * @param {Roo.EventObject} e
5566          */
5567         "beforetoggle" : true
5568     });
5569 };
5570
5571 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5572     
5573     
5574    
5575     // private
5576     navItems : false,
5577     loadMask : false,
5578     
5579     
5580     getAutoCreate : function(){
5581         
5582         
5583         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5584         
5585     },
5586     
5587     initEvents :function ()
5588     {
5589         //Roo.log(this.el.select('.navbar-toggle',true));
5590         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5591         
5592         var mark = {
5593             tag: "div",
5594             cls:"x-dlg-mask"
5595         };
5596         
5597         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5598         
5599         var size = this.el.getSize();
5600         this.maskEl.setSize(size.width, size.height);
5601         this.maskEl.enableDisplayMode("block");
5602         this.maskEl.hide();
5603         
5604         if(this.loadMask){
5605             this.maskEl.show();
5606         }
5607     },
5608     
5609     
5610     getChildContainer : function()
5611     {
5612         if (this.el && this.el.select('.collapse').getCount()) {
5613             return this.el.select('.collapse',true).first();
5614         }
5615         
5616         return this.el;
5617     },
5618     
5619     mask : function()
5620     {
5621         this.maskEl.show();
5622     },
5623     
5624     unmask : function()
5625     {
5626         this.maskEl.hide();
5627     },
5628     onToggle : function()
5629     {
5630         
5631         if(this.fireEvent('beforetoggle', this) === false){
5632             return;
5633         }
5634         var ce = this.el.select('.navbar-collapse',true).first();
5635       
5636         if (!ce.hasClass('show')) {
5637            this.expand();
5638         } else {
5639             this.collapse();
5640         }
5641         
5642         
5643     
5644     },
5645     /**
5646      * Expand the navbar pulldown 
5647      */
5648     expand : function ()
5649     {
5650        
5651         var ce = this.el.select('.navbar-collapse',true).first();
5652         if (ce.hasClass('collapsing')) {
5653             return;
5654         }
5655         ce.dom.style.height = '';
5656                // show it...
5657         ce.addClass('in'); // old...
5658         ce.removeClass('collapse');
5659         ce.addClass('show');
5660         var h = ce.getHeight();
5661         Roo.log(h);
5662         ce.removeClass('show');
5663         // at this point we should be able to see it..
5664         ce.addClass('collapsing');
5665         
5666         ce.setHeight(0); // resize it ...
5667         ce.on('transitionend', function() {
5668             //Roo.log('done transition');
5669             ce.removeClass('collapsing');
5670             ce.addClass('show');
5671             ce.removeClass('collapse');
5672
5673             ce.dom.style.height = '';
5674         }, this, { single: true} );
5675         ce.setHeight(h);
5676         ce.dom.scrollTop = 0;
5677     },
5678     /**
5679      * Collapse the navbar pulldown 
5680      */
5681     collapse : function()
5682     {
5683          var ce = this.el.select('.navbar-collapse',true).first();
5684        
5685         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5686             // it's collapsed or collapsing..
5687             return;
5688         }
5689         ce.removeClass('in'); // old...
5690         ce.setHeight(ce.getHeight());
5691         ce.removeClass('show');
5692         ce.addClass('collapsing');
5693         
5694         ce.on('transitionend', function() {
5695             ce.dom.style.height = '';
5696             ce.removeClass('collapsing');
5697             ce.addClass('collapse');
5698         }, this, { single: true} );
5699         ce.setHeight(0);
5700     }
5701     
5702     
5703     
5704 });
5705
5706
5707
5708  
5709
5710  /*
5711  * - LGPL
5712  *
5713  * navbar
5714  * 
5715  */
5716
5717 /**
5718  * @class Roo.bootstrap.nav.Simplebar
5719  * @extends Roo.bootstrap.nav.Bar
5720  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5721  * Bootstrap Sidebar class
5722  *
5723  * @cfg {Boolean} inverse is inverted color
5724  * 
5725  * @cfg {String} type (nav | pills | tabs)
5726  * @cfg {Boolean} arrangement stacked | justified
5727  * @cfg {String} align (left | right) alignment
5728  * 
5729  * @cfg {Boolean} main (true|false) main nav bar? default false
5730  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5731  * 
5732  * @cfg {String} tag (header|footer|nav|div) default is nav 
5733
5734  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5735  * 
5736  * 
5737  * @constructor
5738  * Create a new Sidebar
5739  * @param {Object} config The config object
5740  */
5741
5742
5743 Roo.bootstrap.nav.Simplebar = function(config){
5744     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5745 };
5746
5747 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5748     
5749     inverse: false,
5750     
5751     type: false,
5752     arrangement: '',
5753     align : false,
5754     
5755     weight : 'light',
5756     
5757     main : false,
5758     
5759     
5760     tag : false,
5761     
5762     
5763     getAutoCreate : function(){
5764         
5765         
5766         var cfg = {
5767             tag : this.tag || 'div',
5768             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5769         };
5770         if (['light','white'].indexOf(this.weight) > -1) {
5771             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5772         }
5773         cfg.cls += ' bg-' + this.weight;
5774         
5775         if (this.inverse) {
5776             cfg.cls += ' navbar-inverse';
5777             
5778         }
5779         
5780         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5781         
5782         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5783             return cfg;
5784         }
5785         
5786         
5787     
5788         
5789         cfg.cn = [
5790             {
5791                 cls: 'nav nav-' + this.xtype,
5792                 tag : 'ul'
5793             }
5794         ];
5795         
5796          
5797         this.type = this.type || 'nav';
5798         if (['tabs','pills'].indexOf(this.type) != -1) {
5799             cfg.cn[0].cls += ' nav-' + this.type
5800         
5801         
5802         } else {
5803             if (this.type!=='nav') {
5804                 Roo.log('nav type must be nav/tabs/pills')
5805             }
5806             cfg.cn[0].cls += ' navbar-nav'
5807         }
5808         
5809         
5810         
5811         
5812         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5813             cfg.cn[0].cls += ' nav-' + this.arrangement;
5814         }
5815         
5816         
5817         if (this.align === 'right') {
5818             cfg.cn[0].cls += ' navbar-right';
5819         }
5820         
5821         
5822         
5823         
5824         return cfg;
5825     
5826         
5827     }
5828     
5829     
5830     
5831 });
5832
5833
5834
5835  
5836
5837  
5838        /*
5839  * - LGPL
5840  *
5841  * navbar
5842  * navbar-fixed-top
5843  * navbar-expand-md  fixed-top 
5844  */
5845
5846 /**
5847  * @class Roo.bootstrap.nav.Headerbar
5848  * @extends Roo.bootstrap.nav.Simplebar
5849  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5850  * Bootstrap Sidebar class
5851  *
5852  * @cfg {String} brand what is brand
5853  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5854  * @cfg {String} brand_href href of the brand
5855  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5856  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5857  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5858  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5859  * 
5860  * @constructor
5861  * Create a new Sidebar
5862  * @param {Object} config The config object
5863  */
5864
5865
5866 Roo.bootstrap.nav.Headerbar = function(config){
5867     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5868       
5869 };
5870
5871 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5872     
5873     position: '',
5874     brand: '',
5875     brand_href: false,
5876     srButton : true,
5877     autohide : false,
5878     desktopCenter : false,
5879    
5880     
5881     getAutoCreate : function(){
5882         
5883         var   cfg = {
5884             tag: this.nav || 'nav',
5885             cls: 'navbar navbar-expand-md',
5886             role: 'navigation',
5887             cn: []
5888         };
5889         
5890         var cn = cfg.cn;
5891         if (this.desktopCenter) {
5892             cn.push({cls : 'container', cn : []});
5893             cn = cn[0].cn;
5894         }
5895         
5896         if(this.srButton){
5897             var btn = {
5898                 tag: 'button',
5899                 type: 'button',
5900                 cls: 'navbar-toggle navbar-toggler',
5901                 'data-toggle': 'collapse',
5902                 cn: [
5903                     {
5904                         tag: 'span',
5905                         cls: 'sr-only',
5906                         html: 'Toggle navigation'
5907                     },
5908                     {
5909                         tag: 'span',
5910                         cls: 'icon-bar navbar-toggler-icon'
5911                     },
5912                     {
5913                         tag: 'span',
5914                         cls: 'icon-bar'
5915                     },
5916                     {
5917                         tag: 'span',
5918                         cls: 'icon-bar'
5919                     }
5920                 ]
5921             };
5922             
5923             cn.push( Roo.bootstrap.version == 4 ? btn : {
5924                 tag: 'div',
5925                 cls: 'navbar-header',
5926                 cn: [
5927                     btn
5928                 ]
5929             });
5930         }
5931         
5932         cn.push({
5933             tag: 'div',
5934             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5935             cn : []
5936         });
5937         
5938         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5939         
5940         if (['light','white'].indexOf(this.weight) > -1) {
5941             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5942         }
5943         cfg.cls += ' bg-' + this.weight;
5944         
5945         
5946         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5947             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5948             
5949             // tag can override this..
5950             
5951             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5952         }
5953         
5954         if (this.brand !== '') {
5955             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5956             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5957                 tag: 'a',
5958                 href: this.brand_href ? this.brand_href : '#',
5959                 cls: 'navbar-brand',
5960                 cn: [
5961                 this.brand
5962                 ]
5963             });
5964         }
5965         
5966         if(this.main){
5967             cfg.cls += ' main-nav';
5968         }
5969         
5970         
5971         return cfg;
5972
5973         
5974     },
5975     getHeaderChildContainer : function()
5976     {
5977         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5978             return this.el.select('.navbar-header',true).first();
5979         }
5980         
5981         return this.getChildContainer();
5982     },
5983     
5984     getChildContainer : function()
5985     {
5986          
5987         return this.el.select('.roo-navbar-collapse',true).first();
5988          
5989         
5990     },
5991     
5992     initEvents : function()
5993     {
5994         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5995         
5996         if (this.autohide) {
5997             
5998             var prevScroll = 0;
5999             var ft = this.el;
6000             
6001             Roo.get(document).on('scroll',function(e) {
6002                 var ns = Roo.get(document).getScroll().top;
6003                 var os = prevScroll;
6004                 prevScroll = ns;
6005                 
6006                 if(ns > os){
6007                     ft.removeClass('slideDown');
6008                     ft.addClass('slideUp');
6009                     return;
6010                 }
6011                 ft.removeClass('slideUp');
6012                 ft.addClass('slideDown');
6013                  
6014               
6015           },this);
6016         }
6017     }    
6018     
6019 });
6020
6021
6022
6023  
6024
6025  /*
6026  * - LGPL
6027  *
6028  * navbar
6029  * 
6030  */
6031
6032 /**
6033  * @class Roo.bootstrap.nav.Sidebar
6034  * @extends Roo.bootstrap.nav.Bar
6035  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6036  * Bootstrap Sidebar class
6037  * 
6038  * @constructor
6039  * Create a new Sidebar
6040  * @param {Object} config The config object
6041  */
6042
6043
6044 Roo.bootstrap.nav.Sidebar = function(config){
6045     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6046 };
6047
6048 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6049     
6050     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6051     
6052     getAutoCreate : function(){
6053         
6054         
6055         return  {
6056             tag: 'div',
6057             cls: 'sidebar sidebar-nav'
6058         };
6059     
6060         
6061     }
6062     
6063     
6064     
6065 });
6066
6067
6068
6069  
6070
6071  /*
6072  * - LGPL
6073  *
6074  * nav group
6075  * 
6076  */
6077
6078 /**
6079  * @class Roo.bootstrap.nav.Group
6080  * @extends Roo.bootstrap.Component
6081  * @children Roo.bootstrap.nav.Item
6082  * Bootstrap NavGroup class
6083  * @cfg {String} align (left|right)
6084  * @cfg {Boolean} inverse
6085  * @cfg {String} type (nav|pills|tab) default nav
6086  * @cfg {String} navId - reference Id for navbar.
6087  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6088  * 
6089  * @constructor
6090  * Create a new nav group
6091  * @param {Object} config The config object
6092  */
6093
6094 Roo.bootstrap.nav.Group = function(config){
6095     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6096     this.navItems = [];
6097    
6098     Roo.bootstrap.nav.Group.register(this);
6099      this.addEvents({
6100         /**
6101              * @event changed
6102              * Fires when the active item changes
6103              * @param {Roo.bootstrap.nav.Group} this
6104              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6105              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6106          */
6107         'changed': true
6108      });
6109     
6110 };
6111
6112 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6113     
6114     align: '',
6115     inverse: false,
6116     form: false,
6117     type: 'nav',
6118     navId : '',
6119     // private
6120     pilltype : true,
6121     
6122     navItems : false, 
6123     
6124     getAutoCreate : function()
6125     {
6126         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6127         
6128         cfg = {
6129             tag : 'ul',
6130             cls: 'nav' 
6131         };
6132         if (Roo.bootstrap.version == 4) {
6133             if (['tabs','pills'].indexOf(this.type) != -1) {
6134                 cfg.cls += ' nav-' + this.type; 
6135             } else {
6136                 // trying to remove so header bar can right align top?
6137                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6138                     // do not use on header bar... 
6139                     cfg.cls += ' navbar-nav';
6140                 }
6141             }
6142             
6143         } else {
6144             if (['tabs','pills'].indexOf(this.type) != -1) {
6145                 cfg.cls += ' nav-' + this.type
6146             } else {
6147                 if (this.type !== 'nav') {
6148                     Roo.log('nav type must be nav/tabs/pills')
6149                 }
6150                 cfg.cls += ' navbar-nav'
6151             }
6152         }
6153         
6154         if (this.parent() && this.parent().sidebar) {
6155             cfg = {
6156                 tag: 'ul',
6157                 cls: 'dashboard-menu sidebar-menu'
6158             };
6159             
6160             return cfg;
6161         }
6162         
6163         if (this.form === true) {
6164             cfg = {
6165                 tag: 'form',
6166                 cls: 'navbar-form form-inline'
6167             };
6168             //nav navbar-right ml-md-auto
6169             if (this.align === 'right') {
6170                 cfg.cls += ' navbar-right ml-md-auto';
6171             } else {
6172                 cfg.cls += ' navbar-left';
6173             }
6174         }
6175         
6176         if (this.align === 'right') {
6177             cfg.cls += ' navbar-right ml-md-auto';
6178         } else {
6179             cfg.cls += ' mr-auto';
6180         }
6181         
6182         if (this.inverse) {
6183             cfg.cls += ' navbar-inverse';
6184             
6185         }
6186         
6187         
6188         return cfg;
6189     },
6190     /**
6191     * sets the active Navigation item
6192     * @param {Roo.bootstrap.nav.Item} the new current navitem
6193     */
6194     setActiveItem : function(item)
6195     {
6196         var prev = false;
6197         Roo.each(this.navItems, function(v){
6198             if (v == item) {
6199                 return ;
6200             }
6201             if (v.isActive()) {
6202                 v.setActive(false, true);
6203                 prev = v;
6204                 
6205             }
6206             
6207         });
6208
6209         item.setActive(true, true);
6210         this.fireEvent('changed', this, item, prev);
6211         
6212         
6213     },
6214     /**
6215     * gets the active Navigation item
6216     * @return {Roo.bootstrap.nav.Item} the current navitem
6217     */
6218     getActive : function()
6219     {
6220         
6221         var prev = false;
6222         Roo.each(this.navItems, function(v){
6223             
6224             if (v.isActive()) {
6225                 prev = v;
6226                 
6227             }
6228             
6229         });
6230         return prev;
6231     },
6232     
6233     indexOfNav : function()
6234     {
6235         
6236         var prev = false;
6237         Roo.each(this.navItems, function(v,i){
6238             
6239             if (v.isActive()) {
6240                 prev = i;
6241                 
6242             }
6243             
6244         });
6245         return prev;
6246     },
6247     /**
6248     * adds a Navigation item
6249     * @param {Roo.bootstrap.nav.Item} the navitem to add
6250     */
6251     addItem : function(cfg)
6252     {
6253         if (this.form && Roo.bootstrap.version == 4) {
6254             cfg.tag = 'div';
6255         }
6256         var cn = new Roo.bootstrap.nav.Item(cfg);
6257         this.register(cn);
6258         cn.parentId = this.id;
6259         cn.onRender(this.el, null);
6260         return cn;
6261     },
6262     /**
6263     * register a Navigation item
6264     * @param {Roo.bootstrap.nav.Item} the navitem to add
6265     */
6266     register : function(item)
6267     {
6268         this.navItems.push( item);
6269         item.navId = this.navId;
6270     
6271     },
6272     
6273     /**
6274     * clear all the Navigation item
6275     */
6276    
6277     clearAll : function()
6278     {
6279         this.navItems = [];
6280         this.el.dom.innerHTML = '';
6281     },
6282     
6283     getNavItem: function(tabId)
6284     {
6285         var ret = false;
6286         Roo.each(this.navItems, function(e) {
6287             if (e.tabId == tabId) {
6288                ret =  e;
6289                return false;
6290             }
6291             return true;
6292             
6293         });
6294         return ret;
6295     },
6296     
6297     setActiveNext : function()
6298     {
6299         var i = this.indexOfNav(this.getActive());
6300         if (i > this.navItems.length) {
6301             return;
6302         }
6303         this.setActiveItem(this.navItems[i+1]);
6304     },
6305     setActivePrev : function()
6306     {
6307         var i = this.indexOfNav(this.getActive());
6308         if (i  < 1) {
6309             return;
6310         }
6311         this.setActiveItem(this.navItems[i-1]);
6312     },
6313     clearWasActive : function(except) {
6314         Roo.each(this.navItems, function(e) {
6315             if (e.tabId != except.tabId && e.was_active) {
6316                e.was_active = false;
6317                return false;
6318             }
6319             return true;
6320             
6321         });
6322     },
6323     getWasActive : function ()
6324     {
6325         var r = false;
6326         Roo.each(this.navItems, function(e) {
6327             if (e.was_active) {
6328                r = e;
6329                return false;
6330             }
6331             return true;
6332             
6333         });
6334         return r;
6335     }
6336     
6337     
6338 });
6339
6340  
6341 Roo.apply(Roo.bootstrap.nav.Group, {
6342     
6343     groups: {},
6344      /**
6345     * register a Navigation Group
6346     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6347     */
6348     register : function(navgrp)
6349     {
6350         this.groups[navgrp.navId] = navgrp;
6351         
6352     },
6353     /**
6354     * fetch a Navigation Group based on the navigation ID
6355     * @param {string} the navgroup to add
6356     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6357     */
6358     get: function(navId) {
6359         if (typeof(this.groups[navId]) == 'undefined') {
6360             return false;
6361             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6362         }
6363         return this.groups[navId] ;
6364     }
6365     
6366     
6367     
6368 });
6369
6370  /**
6371  * @class Roo.bootstrap.nav.Item
6372  * @extends Roo.bootstrap.Component
6373  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6374  * @parent Roo.bootstrap.nav.Group
6375  * @licence LGPL
6376  * Bootstrap Navbar.NavItem class
6377  * 
6378  * @cfg {String} href  link to
6379  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6380  * @cfg {Boolean} button_outline show and outlined button
6381  * @cfg {String} html content of button
6382  * @cfg {String} badge text inside badge
6383  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6384  * @cfg {String} glyphicon DEPRICATED - use fa
6385  * @cfg {String} icon DEPRICATED - use fa
6386  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6387  * @cfg {Boolean} active Is item active
6388  * @cfg {Boolean} disabled Is item disabled
6389  * @cfg {String} linkcls  Link Class
6390  * @cfg {Boolean} preventDefault (true | false) default false
6391  * @cfg {String} tabId the tab that this item activates.
6392  * @cfg {String} tagtype (a|span) render as a href or span?
6393  * @cfg {Boolean} animateRef (true|false) link to element default false  
6394  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6395   
6396  * @constructor
6397  * Create a new Navbar Item
6398  * @param {Object} config The config object
6399  */
6400 Roo.bootstrap.nav.Item = function(config){
6401     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6402     this.addEvents({
6403         // raw events
6404         /**
6405          * @event click
6406          * The raw click event for the entire grid.
6407          * @param {Roo.EventObject} e
6408          */
6409         "click" : true,
6410          /**
6411             * @event changed
6412             * Fires when the active item active state changes
6413             * @param {Roo.bootstrap.nav.Item} this
6414             * @param {boolean} state the new state
6415              
6416          */
6417         'changed': true,
6418         /**
6419             * @event scrollto
6420             * Fires when scroll to element
6421             * @param {Roo.bootstrap.nav.Item} this
6422             * @param {Object} options
6423             * @param {Roo.EventObject} e
6424              
6425          */
6426         'scrollto': true
6427     });
6428    
6429 };
6430
6431 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6432     
6433     href: false,
6434     html: '',
6435     badge: '',
6436     icon: false,
6437     fa : false,
6438     glyphicon: false,
6439     active: false,
6440     preventDefault : false,
6441     tabId : false,
6442     tagtype : 'a',
6443     tag: 'li',
6444     disabled : false,
6445     animateRef : false,
6446     was_active : false,
6447     button_weight : '',
6448     button_outline : false,
6449     linkcls : '',
6450     navLink: false,
6451     
6452     getAutoCreate : function(){
6453          
6454         var cfg = {
6455             tag: this.tag,
6456             cls: 'nav-item'
6457         };
6458         
6459         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6460         
6461         if (this.active) {
6462             cfg.cls +=  ' active' ;
6463         }
6464         if (this.disabled) {
6465             cfg.cls += ' disabled';
6466         }
6467         
6468         // BS4 only?
6469         if (this.button_weight.length) {
6470             cfg.tag = this.href ? 'a' : 'button';
6471             cfg.html = this.html || '';
6472             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6473             if (this.href) {
6474                 cfg.href = this.href;
6475             }
6476             if (this.fa) {
6477                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6478             } else {
6479                 cfg.cls += " nav-html";
6480             }
6481             
6482             // menu .. should add dropdown-menu class - so no need for carat..
6483             
6484             if (this.badge !== '') {
6485                  
6486                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488             return cfg;
6489         }
6490         
6491         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6492             cfg.cn = [
6493                 {
6494                     tag: this.tagtype,
6495                     href : this.href || "#",
6496                     html: this.html || '',
6497                     cls : ''
6498                 }
6499             ];
6500             if (this.tagtype == 'a') {
6501                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6502         
6503             }
6504             if (this.icon) {
6505                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6506             } else  if (this.fa) {
6507                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6508             } else if(this.glyphicon) {
6509                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6510             } else {
6511                 cfg.cn[0].cls += " nav-html";
6512             }
6513             
6514             if (this.menu) {
6515                 cfg.cn[0].html += " <span class='caret'></span>";
6516              
6517             }
6518             
6519             if (this.badge !== '') {
6520                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6521             }
6522         }
6523         
6524         
6525         
6526         return cfg;
6527     },
6528     onRender : function(ct, position)
6529     {
6530        // Roo.log("Call onRender: " + this.xtype);
6531         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6532             this.tag = 'div';
6533         }
6534         
6535         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6536         this.navLink = this.el.select('.nav-link',true).first();
6537         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6538         return ret;
6539     },
6540       
6541     
6542     initEvents: function() 
6543     {
6544         if (typeof (this.menu) != 'undefined') {
6545             this.menu.parentType = this.xtype;
6546             this.menu.triggerEl = this.el;
6547             this.menu = this.addxtype(Roo.apply({}, this.menu));
6548         }
6549         
6550         this.el.on('click', this.onClick, this);
6551         
6552         //if(this.tagtype == 'span'){
6553         //    this.el.select('span',true).on('click', this.onClick, this);
6554         //}
6555        
6556         // at this point parent should be available..
6557         this.parent().register(this);
6558     },
6559     
6560     onClick : function(e)
6561     {
6562         if (e.getTarget('.dropdown-menu-item')) {
6563             // did you click on a menu itemm.... - then don't trigger onclick..
6564             return;
6565         }
6566         
6567         if(
6568                 this.preventDefault ||
6569                                 this.href === false ||
6570                 this.href === '#' 
6571         ){
6572             //Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574         }
6575         
6576         if (this.disabled) {
6577             return;
6578         }
6579         
6580         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6581         if (tg && tg.transition) {
6582             Roo.log("waiting for the transitionend");
6583             return;
6584         }
6585         
6586         
6587         
6588         //Roo.log("fire event clicked");
6589         if(this.fireEvent('click', this, e) === false){
6590             return;
6591         };
6592         
6593         if(this.tagtype == 'span'){
6594             return;
6595         }
6596         
6597         //Roo.log(this.href);
6598         var ael = this.el.select('a',true).first();
6599         //Roo.log(ael);
6600         
6601         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6602             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6603             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6604                 return; // ignore... - it's a 'hash' to another page.
6605             }
6606             Roo.log("NavItem - prevent Default?");
6607             e.preventDefault();
6608             this.scrollToElement(e);
6609         }
6610         
6611         
6612         var p =  this.parent();
6613    
6614         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6615             if (typeof(p.setActiveItem) !== 'undefined') {
6616                 p.setActiveItem(this);
6617             }
6618         }
6619         
6620         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6621         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6622             // remove the collapsed menu expand...
6623             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6624         }
6625     },
6626     
6627     isActive: function () {
6628         return this.active
6629     },
6630     setActive : function(state, fire, is_was_active)
6631     {
6632         if (this.active && !state && this.navId) {
6633             this.was_active = true;
6634             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6635             if (nv) {
6636                 nv.clearWasActive(this);
6637             }
6638             
6639         }
6640         this.active = state;
6641         
6642         if (!state ) {
6643             this.el.removeClass('active');
6644             this.navLink ? this.navLink.removeClass('active') : false;
6645         } else if (!this.el.hasClass('active')) {
6646             
6647             this.el.addClass('active');
6648             if (Roo.bootstrap.version == 4 && this.navLink ) {
6649                 this.navLink.addClass('active');
6650             }
6651             
6652         }
6653         if (fire) {
6654             this.fireEvent('changed', this, state);
6655         }
6656         
6657         // show a panel if it's registered and related..
6658         
6659         if (!this.navId || !this.tabId || !state || is_was_active) {
6660             return;
6661         }
6662         
6663         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6664         if (!tg) {
6665             return;
6666         }
6667         var pan = tg.getPanelByName(this.tabId);
6668         if (!pan) {
6669             return;
6670         }
6671         // if we can not flip to new panel - go back to old nav highlight..
6672         if (false == tg.showPanel(pan)) {
6673             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6674             if (nv) {
6675                 var onav = nv.getWasActive();
6676                 if (onav) {
6677                     onav.setActive(true, false, true);
6678                 }
6679             }
6680             
6681         }
6682         
6683         
6684         
6685     },
6686      // this should not be here...
6687     setDisabled : function(state)
6688     {
6689         this.disabled = state;
6690         if (!state ) {
6691             this.el.removeClass('disabled');
6692         } else if (!this.el.hasClass('disabled')) {
6693             this.el.addClass('disabled');
6694         }
6695         
6696     },
6697     
6698     /**
6699      * Fetch the element to display the tooltip on.
6700      * @return {Roo.Element} defaults to this.el
6701      */
6702     tooltipEl : function()
6703     {
6704         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6705     },
6706     
6707     scrollToElement : function(e)
6708     {
6709         var c = document.body;
6710         
6711         /*
6712          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6713          */
6714         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6715             c = document.documentElement;
6716         }
6717         
6718         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6719         
6720         if(!target){
6721             return;
6722         }
6723
6724         var o = target.calcOffsetsTo(c);
6725         
6726         var options = {
6727             target : target,
6728             value : o[1]
6729         };
6730         
6731         this.fireEvent('scrollto', this, options, e);
6732         
6733         Roo.get(c).scrollTo('top', options.value, true);
6734         
6735         return;
6736     },
6737     /**
6738      * Set the HTML (text content) of the item
6739      * @param {string} html  content for the nav item
6740      */
6741     setHtml : function(html)
6742     {
6743         this.html = html;
6744         this.htmlEl.dom.innerHTML = html;
6745         
6746     } 
6747 });
6748  
6749
6750  /*
6751  * - LGPL
6752  *
6753  * sidebar item
6754  *
6755  *  li
6756  *    <span> icon </span>
6757  *    <span> text </span>
6758  *    <span>badge </span>
6759  */
6760
6761 /**
6762  * @class Roo.bootstrap.nav.SidebarItem
6763  * @extends Roo.bootstrap.nav.Item
6764  * Bootstrap Navbar.NavSidebarItem class
6765  * 
6766  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6767  * {Boolean} open is the menu open
6768  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6769  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6770  * {String} buttonSize (sm|md|lg)the extra classes for the button
6771  * {Boolean} showArrow show arrow next to the text (default true)
6772  * @constructor
6773  * Create a new Navbar Button
6774  * @param {Object} config The config object
6775  */
6776 Roo.bootstrap.nav.SidebarItem = function(config){
6777     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6778     this.addEvents({
6779         // raw events
6780         /**
6781          * @event click
6782          * The raw click event for the entire grid.
6783          * @param {Roo.EventObject} e
6784          */
6785         "click" : true,
6786          /**
6787             * @event changed
6788             * Fires when the active item active state changes
6789             * @param {Roo.bootstrap.nav.SidebarItem} this
6790             * @param {boolean} state the new state
6791              
6792          */
6793         'changed': true
6794     });
6795    
6796 };
6797
6798 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6799     
6800     badgeWeight : 'default',
6801     
6802     open: false,
6803     
6804     buttonView : false,
6805     
6806     buttonWeight : 'default',
6807     
6808     buttonSize : 'md',
6809     
6810     showArrow : true,
6811     
6812     getAutoCreate : function(){
6813         
6814         
6815         var a = {
6816                 tag: 'a',
6817                 href : this.href || '#',
6818                 cls: '',
6819                 html : '',
6820                 cn : []
6821         };
6822         
6823         if(this.buttonView){
6824             a = {
6825                 tag: 'button',
6826                 href : this.href || '#',
6827                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6828                 html : this.html,
6829                 cn : []
6830             };
6831         }
6832         
6833         var cfg = {
6834             tag: 'li',
6835             cls: '',
6836             cn: [ a ]
6837         };
6838         
6839         if (this.active) {
6840             cfg.cls += ' active';
6841         }
6842         
6843         if (this.disabled) {
6844             cfg.cls += ' disabled';
6845         }
6846         if (this.open) {
6847             cfg.cls += ' open x-open';
6848         }
6849         // left icon..
6850         if (this.glyphicon || this.icon) {
6851             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6852             a.cn.push({ tag : 'i', cls : c }) ;
6853         }
6854         
6855         if(!this.buttonView){
6856             var span = {
6857                 tag: 'span',
6858                 html : this.html || ''
6859             };
6860
6861             a.cn.push(span);
6862             
6863         }
6864         
6865         if (this.badge !== '') {
6866             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6867         }
6868         
6869         if (this.menu) {
6870             
6871             if(this.showArrow){
6872                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6873             }
6874             
6875             a.cls += ' dropdown-toggle treeview' ;
6876         }
6877         
6878         return cfg;
6879     },
6880     
6881     initEvents : function()
6882     { 
6883         if (typeof (this.menu) != 'undefined') {
6884             this.menu.parentType = this.xtype;
6885             this.menu.triggerEl = this.el;
6886             this.menu = this.addxtype(Roo.apply({}, this.menu));
6887         }
6888         
6889         this.el.on('click', this.onClick, this);
6890         
6891         if(this.badge !== ''){
6892             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6893         }
6894         
6895     },
6896     
6897     onClick : function(e)
6898     {
6899         if(this.disabled){
6900             e.preventDefault();
6901             return;
6902         }
6903         
6904         if(this.preventDefault){
6905             e.preventDefault();
6906         }
6907         
6908         this.fireEvent('click', this, e);
6909     },
6910     
6911     disable : function()
6912     {
6913         this.setDisabled(true);
6914     },
6915     
6916     enable : function()
6917     {
6918         this.setDisabled(false);
6919     },
6920     
6921     setDisabled : function(state)
6922     {
6923         if(this.disabled == state){
6924             return;
6925         }
6926         
6927         this.disabled = state;
6928         
6929         if (state) {
6930             this.el.addClass('disabled');
6931             return;
6932         }
6933         
6934         this.el.removeClass('disabled');
6935         
6936         return;
6937     },
6938     
6939     setActive : function(state)
6940     {
6941         if(this.active == state){
6942             return;
6943         }
6944         
6945         this.active = state;
6946         
6947         if (state) {
6948             this.el.addClass('active');
6949             return;
6950         }
6951         
6952         this.el.removeClass('active');
6953         
6954         return;
6955     },
6956     
6957     isActive: function () 
6958     {
6959         return this.active;
6960     },
6961     
6962     setBadge : function(str)
6963     {
6964         if(!this.badgeEl){
6965             return;
6966         }
6967         
6968         this.badgeEl.dom.innerHTML = str;
6969     }
6970     
6971    
6972      
6973  
6974 });
6975  
6976
6977  /*
6978  * - LGPL
6979  *
6980  * nav progress bar
6981  * 
6982  */
6983
6984 /**
6985  * @class Roo.bootstrap.nav.ProgressBar
6986  * @extends Roo.bootstrap.Component
6987  * @children Roo.bootstrap.nav.ProgressBarItem
6988  * Bootstrap NavProgressBar class
6989  * 
6990  * @constructor
6991  * Create a new nav progress bar - a bar indicating step along a process
6992  * @param {Object} config The config object
6993  */
6994
6995 Roo.bootstrap.nav.ProgressBar = function(config){
6996     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6997
6998     this.bullets = this.bullets || [];
6999    
7000 //    Roo.bootstrap.nav.ProgressBar.register(this);
7001      this.addEvents({
7002         /**
7003              * @event changed
7004              * Fires when the active item changes
7005              * @param {Roo.bootstrap.nav.ProgressBar} this
7006              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7007              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7008          */
7009         'changed': true
7010      });
7011     
7012 };
7013
7014 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7015     /**
7016      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7017      * Bullets for the Nav Progress bar for the toolbar
7018      */
7019     bullets : [],
7020     barItems : [],
7021     
7022     getAutoCreate : function()
7023     {
7024         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7025         
7026         cfg = {
7027             tag : 'div',
7028             cls : 'roo-navigation-bar-group',
7029             cn : [
7030                 {
7031                     tag : 'div',
7032                     cls : 'roo-navigation-top-bar'
7033                 },
7034                 {
7035                     tag : 'div',
7036                     cls : 'roo-navigation-bullets-bar',
7037                     cn : [
7038                         {
7039                             tag : 'ul',
7040                             cls : 'roo-navigation-bar'
7041                         }
7042                     ]
7043                 },
7044                 
7045                 {
7046                     tag : 'div',
7047                     cls : 'roo-navigation-bottom-bar'
7048                 }
7049             ]
7050             
7051         };
7052         
7053         return cfg;
7054         
7055     },
7056     
7057     initEvents: function() 
7058     {
7059         
7060     },
7061     
7062     onRender : function(ct, position) 
7063     {
7064         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7065         
7066         if(this.bullets.length){
7067             Roo.each(this.bullets, function(b){
7068                this.addItem(b);
7069             }, this);
7070         }
7071         
7072         this.format();
7073         
7074     },
7075     
7076     addItem : function(cfg)
7077     {
7078         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7079         
7080         item.parentId = this.id;
7081         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7082         
7083         if(cfg.html){
7084             var top = new Roo.bootstrap.Element({
7085                 tag : 'div',
7086                 cls : 'roo-navigation-bar-text'
7087             });
7088             
7089             var bottom = new Roo.bootstrap.Element({
7090                 tag : 'div',
7091                 cls : 'roo-navigation-bar-text'
7092             });
7093             
7094             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7095             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7096             
7097             var topText = new Roo.bootstrap.Element({
7098                 tag : 'span',
7099                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7100             });
7101             
7102             var bottomText = new Roo.bootstrap.Element({
7103                 tag : 'span',
7104                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7105             });
7106             
7107             topText.onRender(top.el, null);
7108             bottomText.onRender(bottom.el, null);
7109             
7110             item.topEl = top;
7111             item.bottomEl = bottom;
7112         }
7113         
7114         this.barItems.push(item);
7115         
7116         return item;
7117     },
7118     
7119     getActive : function()
7120     {
7121         var active = false;
7122         
7123         Roo.each(this.barItems, function(v){
7124             
7125             if (!v.isActive()) {
7126                 return;
7127             }
7128             
7129             active = v;
7130             return false;
7131             
7132         });
7133         
7134         return active;
7135     },
7136     
7137     setActiveItem : function(item)
7138     {
7139         var prev = false;
7140         
7141         Roo.each(this.barItems, function(v){
7142             if (v.rid == item.rid) {
7143                 return ;
7144             }
7145             
7146             if (v.isActive()) {
7147                 v.setActive(false);
7148                 prev = v;
7149             }
7150         });
7151
7152         item.setActive(true);
7153         
7154         this.fireEvent('changed', this, item, prev);
7155     },
7156     
7157     getBarItem: function(rid)
7158     {
7159         var ret = false;
7160         
7161         Roo.each(this.barItems, function(e) {
7162             if (e.rid != rid) {
7163                 return;
7164             }
7165             
7166             ret =  e;
7167             return false;
7168         });
7169         
7170         return ret;
7171     },
7172     
7173     indexOfItem : function(item)
7174     {
7175         var index = false;
7176         
7177         Roo.each(this.barItems, function(v, i){
7178             
7179             if (v.rid != item.rid) {
7180                 return;
7181             }
7182             
7183             index = i;
7184             return false
7185         });
7186         
7187         return index;
7188     },
7189     
7190     setActiveNext : function()
7191     {
7192         var i = this.indexOfItem(this.getActive());
7193         
7194         if (i > this.barItems.length) {
7195             return;
7196         }
7197         
7198         this.setActiveItem(this.barItems[i+1]);
7199     },
7200     
7201     setActivePrev : function()
7202     {
7203         var i = this.indexOfItem(this.getActive());
7204         
7205         if (i  < 1) {
7206             return;
7207         }
7208         
7209         this.setActiveItem(this.barItems[i-1]);
7210     },
7211     
7212     format : function()
7213     {
7214         if(!this.barItems.length){
7215             return;
7216         }
7217      
7218         var width = 100 / this.barItems.length;
7219         
7220         Roo.each(this.barItems, function(i){
7221             i.el.setStyle('width', width + '%');
7222             i.topEl.el.setStyle('width', width + '%');
7223             i.bottomEl.el.setStyle('width', width + '%');
7224         }, this);
7225         
7226     }
7227     
7228 });
7229 /*
7230  * - LGPL
7231  *
7232  * Nav Progress Item
7233  * 
7234  */
7235
7236 /**
7237  * @class Roo.bootstrap.nav.ProgressBarItem
7238  * @extends Roo.bootstrap.Component
7239  * Bootstrap NavProgressBarItem class
7240  * @cfg {String} rid the reference id
7241  * @cfg {Boolean} active (true|false) Is item active default false
7242  * @cfg {Boolean} disabled (true|false) Is item active default false
7243  * @cfg {String} html
7244  * @cfg {String} position (top|bottom) text position default bottom
7245  * @cfg {String} icon show icon instead of number
7246  * 
7247  * @constructor
7248  * Create a new NavProgressBarItem
7249  * @param {Object} config The config object
7250  */
7251 Roo.bootstrap.nav.ProgressBarItem = function(config){
7252     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7253     this.addEvents({
7254         // raw events
7255         /**
7256          * @event click
7257          * The raw click event for the entire grid.
7258          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7259          * @param {Roo.EventObject} e
7260          */
7261         "click" : true
7262     });
7263    
7264 };
7265
7266 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7267     
7268     rid : '',
7269     active : false,
7270     disabled : false,
7271     html : '',
7272     position : 'bottom',
7273     icon : false,
7274     
7275     getAutoCreate : function()
7276     {
7277         var iconCls = 'roo-navigation-bar-item-icon';
7278         
7279         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7280         
7281         var cfg = {
7282             tag: 'li',
7283             cls: 'roo-navigation-bar-item',
7284             cn : [
7285                 {
7286                     tag : 'i',
7287                     cls : iconCls
7288                 }
7289             ]
7290         };
7291         
7292         if(this.active){
7293             cfg.cls += ' active';
7294         }
7295         if(this.disabled){
7296             cfg.cls += ' disabled';
7297         }
7298         
7299         return cfg;
7300     },
7301     
7302     disable : function()
7303     {
7304         this.setDisabled(true);
7305     },
7306     
7307     enable : function()
7308     {
7309         this.setDisabled(false);
7310     },
7311     
7312     initEvents: function() 
7313     {
7314         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7315         
7316         this.iconEl.on('click', this.onClick, this);
7317     },
7318     
7319     onClick : function(e)
7320     {
7321         e.preventDefault();
7322         
7323         if(this.disabled){
7324             return;
7325         }
7326         
7327         if(this.fireEvent('click', this, e) === false){
7328             return;
7329         };
7330         
7331         this.parent().setActiveItem(this);
7332     },
7333     
7334     isActive: function () 
7335     {
7336         return this.active;
7337     },
7338     
7339     setActive : function(state)
7340     {
7341         if(this.active == state){
7342             return;
7343         }
7344         
7345         this.active = state;
7346         
7347         if (state) {
7348             this.el.addClass('active');
7349             return;
7350         }
7351         
7352         this.el.removeClass('active');
7353         
7354         return;
7355     },
7356     
7357     setDisabled : function(state)
7358     {
7359         if(this.disabled == state){
7360             return;
7361         }
7362         
7363         this.disabled = state;
7364         
7365         if (state) {
7366             this.el.addClass('disabled');
7367             return;
7368         }
7369         
7370         this.el.removeClass('disabled');
7371     },
7372     
7373     tooltipEl : function()
7374     {
7375         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7376     }
7377 });
7378  
7379
7380  /*
7381  * - LGPL
7382  *
7383  *  Breadcrumb Nav
7384  * 
7385  */
7386 Roo.namespace('Roo.bootstrap.breadcrumb');
7387
7388
7389 /**
7390  * @class Roo.bootstrap.breadcrumb.Nav
7391  * @extends Roo.bootstrap.Component
7392  * Bootstrap Breadcrumb Nav Class
7393  *  
7394  * @children Roo.bootstrap.breadcrumb.Item
7395  * 
7396  * @constructor
7397  * Create a new breadcrumb.Nav
7398  * @param {Object} config The config object
7399  */
7400
7401
7402 Roo.bootstrap.breadcrumb.Nav = function(config){
7403     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7404     
7405     
7406 };
7407
7408 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7409     
7410     getAutoCreate : function()
7411     {
7412
7413         var cfg = {
7414             tag: 'nav',
7415             cn : [
7416                 {
7417                     tag : 'ol',
7418                     cls : 'breadcrumb'
7419                 }
7420             ]
7421             
7422         };
7423           
7424         return cfg;
7425     },
7426     
7427     initEvents: function()
7428     {
7429         this.olEl = this.el.select('ol',true).first();    
7430     },
7431     getChildContainer : function()
7432     {
7433         return this.olEl;  
7434     }
7435     
7436 });
7437
7438  /*
7439  * - LGPL
7440  *
7441  *  Breadcrumb Item
7442  * 
7443  */
7444
7445
7446 /**
7447  * @class Roo.bootstrap.breadcrumb.Nav
7448  * @extends Roo.bootstrap.Component
7449  * @children Roo.bootstrap.Component
7450  * @parent Roo.bootstrap.breadcrumb.Nav
7451  * Bootstrap Breadcrumb Nav Class
7452  *  
7453  * 
7454  * @cfg {String} html the content of the link.
7455  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7456  * @cfg {Boolean} active is it active
7457
7458  * 
7459  * @constructor
7460  * Create a new breadcrumb.Nav
7461  * @param {Object} config The config object
7462  */
7463
7464 Roo.bootstrap.breadcrumb.Item = function(config){
7465     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7466     this.addEvents({
7467         // img events
7468         /**
7469          * @event click
7470          * The img click event for the img.
7471          * @param {Roo.EventObject} e
7472          */
7473         "click" : true
7474     });
7475     
7476 };
7477
7478 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7479     
7480     href: false,
7481     html : '',
7482     
7483     getAutoCreate : function()
7484     {
7485
7486         var cfg = {
7487             tag: 'li',
7488             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7489         };
7490         if (this.href !== false) {
7491             cfg.cn = [{
7492                 tag : 'a',
7493                 href : this.href,
7494                 html : this.html
7495             }];
7496         } else {
7497             cfg.html = this.html;
7498         }
7499         
7500         return cfg;
7501     },
7502     
7503     initEvents: function()
7504     {
7505         if (this.href) {
7506             this.el.select('a', true).first().on('click',this.onClick, this)
7507         }
7508         
7509     },
7510     onClick : function(e)
7511     {
7512         e.preventDefault();
7513         this.fireEvent('click',this,  e);
7514     }
7515     
7516 });
7517
7518  /*
7519  * - LGPL
7520  *
7521  * row
7522  * 
7523  */
7524
7525 /**
7526  * @class Roo.bootstrap.Row
7527  * @extends Roo.bootstrap.Component
7528  * @children Roo.bootstrap.Component
7529  * Bootstrap Row class (contains columns...)
7530  * 
7531  * @constructor
7532  * Create a new Row
7533  * @param {Object} config The config object
7534  */
7535
7536 Roo.bootstrap.Row = function(config){
7537     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7538 };
7539
7540 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7541     
7542     getAutoCreate : function(){
7543        return {
7544             cls: 'row clearfix'
7545        };
7546     }
7547     
7548     
7549 });
7550
7551  
7552
7553  /*
7554  * - LGPL
7555  *
7556  * pagination
7557  * 
7558  */
7559
7560 /**
7561  * @class Roo.bootstrap.Pagination
7562  * @extends Roo.bootstrap.Component
7563  * @children Roo.bootstrap.Pagination
7564  * Bootstrap Pagination class
7565  * 
7566  * @cfg {String} size (xs|sm|md|lg|xl)
7567  * @cfg {Boolean} inverse 
7568  * 
7569  * @constructor
7570  * Create a new Pagination
7571  * @param {Object} config The config object
7572  */
7573
7574 Roo.bootstrap.Pagination = function(config){
7575     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7576 };
7577
7578 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7579     
7580     cls: false,
7581     size: false,
7582     inverse: false,
7583     
7584     getAutoCreate : function(){
7585         var cfg = {
7586             tag: 'ul',
7587                 cls: 'pagination'
7588         };
7589         if (this.inverse) {
7590             cfg.cls += ' inverse';
7591         }
7592         if (this.html) {
7593             cfg.html=this.html;
7594         }
7595         if (this.cls) {
7596             cfg.cls += " " + this.cls;
7597         }
7598         return cfg;
7599     }
7600    
7601 });
7602
7603  
7604
7605  /*
7606  * - LGPL
7607  *
7608  * Pagination item
7609  * 
7610  */
7611
7612
7613 /**
7614  * @class Roo.bootstrap.PaginationItem
7615  * @extends Roo.bootstrap.Component
7616  * Bootstrap PaginationItem class
7617  * @cfg {String} html text
7618  * @cfg {String} href the link
7619  * @cfg {Boolean} preventDefault (true | false) default true
7620  * @cfg {Boolean} active (true | false) default false
7621  * @cfg {Boolean} disabled default false
7622  * 
7623  * 
7624  * @constructor
7625  * Create a new PaginationItem
7626  * @param {Object} config The config object
7627  */
7628
7629
7630 Roo.bootstrap.PaginationItem = function(config){
7631     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7632     this.addEvents({
7633         // raw events
7634         /**
7635          * @event click
7636          * The raw click event for the entire grid.
7637          * @param {Roo.EventObject} e
7638          */
7639         "click" : true
7640     });
7641 };
7642
7643 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7644     
7645     href : false,
7646     html : false,
7647     preventDefault: true,
7648     active : false,
7649     cls : false,
7650     disabled: false,
7651     
7652     getAutoCreate : function(){
7653         var cfg= {
7654             tag: 'li',
7655             cn: [
7656                 {
7657                     tag : 'a',
7658                     href : this.href ? this.href : '#',
7659                     html : this.html ? this.html : ''
7660                 }
7661             ]
7662         };
7663         
7664         if(this.cls){
7665             cfg.cls = this.cls;
7666         }
7667         
7668         if(this.disabled){
7669             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7670         }
7671         
7672         if(this.active){
7673             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7674         }
7675         
7676         return cfg;
7677     },
7678     
7679     initEvents: function() {
7680         
7681         this.el.on('click', this.onClick, this);
7682         
7683     },
7684     onClick : function(e)
7685     {
7686         Roo.log('PaginationItem on click ');
7687         if(this.preventDefault){
7688             e.preventDefault();
7689         }
7690         
7691         if(this.disabled){
7692             return;
7693         }
7694         
7695         this.fireEvent('click', this, e);
7696     }
7697    
7698 });
7699
7700  
7701
7702  /*
7703  * - LGPL
7704  *
7705  * slider
7706  * 
7707  */
7708
7709
7710 /**
7711  * @class Roo.bootstrap.Slider
7712  * @extends Roo.bootstrap.Component
7713  * Bootstrap Slider class
7714  *    
7715  * @constructor
7716  * Create a new Slider
7717  * @param {Object} config The config object
7718  */
7719
7720 Roo.bootstrap.Slider = function(config){
7721     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7722 };
7723
7724 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7725     
7726     getAutoCreate : function(){
7727         
7728         var cfg = {
7729             tag: 'div',
7730             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7731             cn: [
7732                 {
7733                     tag: 'a',
7734                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7735                 }
7736             ]
7737         };
7738         
7739         return cfg;
7740     }
7741    
7742 });
7743
7744  /*
7745  * Based on:
7746  * Ext JS Library 1.1.1
7747  * Copyright(c) 2006-2007, Ext JS, LLC.
7748  *
7749  * Originally Released Under LGPL - original licence link has changed is not relivant.
7750  *
7751  * Fork - LGPL
7752  * <script type="text/javascript">
7753  */
7754  /**
7755  * @extends Roo.dd.DDProxy
7756  * @class Roo.grid.SplitDragZone
7757  * Support for Column Header resizing
7758  * @constructor
7759  * @param {Object} config
7760  */
7761 // private
7762 // This is a support class used internally by the Grid components
7763 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7764     this.grid = grid;
7765     this.view = grid.getView();
7766     this.proxy = this.view.resizeProxy;
7767     Roo.grid.SplitDragZone.superclass.constructor.call(
7768         this,
7769         hd, // ID
7770         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7771         {  // CONFIG
7772             dragElId : Roo.id(this.proxy.dom),
7773             resizeFrame:false
7774         }
7775     );
7776     
7777     this.setHandleElId(Roo.id(hd));
7778     if (hd2 !== false) {
7779         this.setOuterHandleElId(Roo.id(hd2));
7780     }
7781     
7782     this.scroll = false;
7783 };
7784 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7785     fly: Roo.Element.fly,
7786
7787     b4StartDrag : function(x, y){
7788         this.view.headersDisabled = true;
7789         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7790                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7791         );
7792         this.proxy.setHeight(h);
7793         
7794         // for old system colWidth really stored the actual width?
7795         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7796         // which in reality did not work.. - it worked only for fixed sizes
7797         // for resizable we need to use actual sizes.
7798         var w = this.cm.getColumnWidth(this.cellIndex);
7799         if (!this.view.mainWrap) {
7800             // bootstrap.
7801             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7802         }
7803         
7804         
7805         
7806         // this was w-this.grid.minColumnWidth;
7807         // doesnt really make sense? - w = thie curren width or the rendered one?
7808         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7809         this.resetConstraints();
7810         this.setXConstraint(minw, 1000);
7811         this.setYConstraint(0, 0);
7812         this.minX = x - minw;
7813         this.maxX = x + 1000;
7814         this.startPos = x;
7815         if (!this.view.mainWrap) { // this is Bootstrap code..
7816             this.getDragEl().style.display='block';
7817         }
7818         
7819         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7820     },
7821
7822
7823     handleMouseDown : function(e){
7824         ev = Roo.EventObject.setEvent(e);
7825         var t = this.fly(ev.getTarget());
7826         if(t.hasClass("x-grid-split")){
7827             this.cellIndex = this.view.getCellIndex(t.dom);
7828             this.split = t.dom;
7829             this.cm = this.grid.colModel;
7830             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7831                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7832             }
7833         }
7834     },
7835
7836     endDrag : function(e){
7837         this.view.headersDisabled = false;
7838         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7839         var diff = endX - this.startPos;
7840         // 
7841         var w = this.cm.getColumnWidth(this.cellIndex);
7842         if (!this.view.mainWrap) {
7843             w = 0;
7844         }
7845         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7846     },
7847
7848     autoOffset : function(){
7849         this.setDelta(0,0);
7850     }
7851 });/*
7852  * Based on:
7853  * Ext JS Library 1.1.1
7854  * Copyright(c) 2006-2007, Ext JS, LLC.
7855  *
7856  * Originally Released Under LGPL - original licence link has changed is not relivant.
7857  *
7858  * Fork - LGPL
7859  * <script type="text/javascript">
7860  */
7861
7862 /**
7863  * @class Roo.grid.AbstractSelectionModel
7864  * @extends Roo.util.Observable
7865  * @abstract
7866  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7867  * implemented by descendant classes.  This class should not be directly instantiated.
7868  * @constructor
7869  */
7870 Roo.grid.AbstractSelectionModel = function(){
7871     this.locked = false;
7872     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7873 };
7874
7875 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7876     /** @ignore Called by the grid automatically. Do not call directly. */
7877     init : function(grid){
7878         this.grid = grid;
7879         this.initEvents();
7880     },
7881
7882     /**
7883      * Locks the selections.
7884      */
7885     lock : function(){
7886         this.locked = true;
7887     },
7888
7889     /**
7890      * Unlocks the selections.
7891      */
7892     unlock : function(){
7893         this.locked = false;
7894     },
7895
7896     /**
7897      * Returns true if the selections are locked.
7898      * @return {Boolean}
7899      */
7900     isLocked : function(){
7901         return this.locked;
7902     }
7903 });/*
7904  * Based on:
7905  * Ext JS Library 1.1.1
7906  * Copyright(c) 2006-2007, Ext JS, LLC.
7907  *
7908  * Originally Released Under LGPL - original licence link has changed is not relivant.
7909  *
7910  * Fork - LGPL
7911  * <script type="text/javascript">
7912  */
7913 /**
7914  * @extends Roo.grid.AbstractSelectionModel
7915  * @class Roo.grid.RowSelectionModel
7916  * The default SelectionModel used by {@link Roo.grid.Grid}.
7917  * It supports multiple selections and keyboard selection/navigation. 
7918  * @constructor
7919  * @param {Object} config
7920  */
7921 Roo.grid.RowSelectionModel = function(config){
7922     Roo.apply(this, config);
7923     this.selections = new Roo.util.MixedCollection(false, function(o){
7924         return o.id;
7925     });
7926
7927     this.last = false;
7928     this.lastActive = false;
7929
7930     this.addEvents({
7931         /**
7932         * @event selectionchange
7933         * Fires when the selection changes
7934         * @param {SelectionModel} this
7935         */
7936        "selectionchange" : true,
7937        /**
7938         * @event afterselectionchange
7939         * Fires after the selection changes (eg. by key press or clicking)
7940         * @param {SelectionModel} this
7941         */
7942        "afterselectionchange" : true,
7943        /**
7944         * @event beforerowselect
7945         * Fires when a row is selected being selected, return false to cancel.
7946         * @param {SelectionModel} this
7947         * @param {Number} rowIndex The selected index
7948         * @param {Boolean} keepExisting False if other selections will be cleared
7949         */
7950        "beforerowselect" : true,
7951        /**
7952         * @event rowselect
7953         * Fires when a row is selected.
7954         * @param {SelectionModel} this
7955         * @param {Number} rowIndex The selected index
7956         * @param {Roo.data.Record} r The record
7957         */
7958        "rowselect" : true,
7959        /**
7960         * @event rowdeselect
7961         * Fires when a row is deselected.
7962         * @param {SelectionModel} this
7963         * @param {Number} rowIndex The selected index
7964         */
7965         "rowdeselect" : true
7966     });
7967     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7968     this.locked = false;
7969 };
7970
7971 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7972     /**
7973      * @cfg {Boolean} singleSelect
7974      * True to allow selection of only one row at a time (defaults to false)
7975      */
7976     singleSelect : false,
7977
7978     // private
7979     initEvents : function(){
7980
7981         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7982             this.grid.on("mousedown", this.handleMouseDown, this);
7983         }else{ // allow click to work like normal
7984             this.grid.on("rowclick", this.handleDragableRowClick, this);
7985         }
7986         // bootstrap does not have a view..
7987         var view = this.grid.view ? this.grid.view : this.grid;
7988         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7989             "up" : function(e){
7990                 if(!e.shiftKey){
7991                     this.selectPrevious(e.shiftKey);
7992                 }else if(this.last !== false && this.lastActive !== false){
7993                     var last = this.last;
7994                     this.selectRange(this.last,  this.lastActive-1);
7995                     view.focusRow(this.lastActive);
7996                     if(last !== false){
7997                         this.last = last;
7998                     }
7999                 }else{
8000                     this.selectFirstRow();
8001                 }
8002                 this.fireEvent("afterselectionchange", this);
8003             },
8004             "down" : function(e){
8005                 if(!e.shiftKey){
8006                     this.selectNext(e.shiftKey);
8007                 }else if(this.last !== false && this.lastActive !== false){
8008                     var last = this.last;
8009                     this.selectRange(this.last,  this.lastActive+1);
8010                     view.focusRow(this.lastActive);
8011                     if(last !== false){
8012                         this.last = last;
8013                     }
8014                 }else{
8015                     this.selectFirstRow();
8016                 }
8017                 this.fireEvent("afterselectionchange", this);
8018             },
8019             scope: this
8020         });
8021
8022          
8023         view.on("refresh", this.onRefresh, this);
8024         view.on("rowupdated", this.onRowUpdated, this);
8025         view.on("rowremoved", this.onRemove, this);
8026     },
8027
8028     // private
8029     onRefresh : function(){
8030         var ds = this.grid.ds, i, v = this.grid.view;
8031         var s = this.selections;
8032         s.each(function(r){
8033             if((i = ds.indexOfId(r.id)) != -1){
8034                 v.onRowSelect(i);
8035                 s.add(ds.getAt(i)); // updating the selection relate data
8036             }else{
8037                 s.remove(r);
8038             }
8039         });
8040     },
8041
8042     // private
8043     onRemove : function(v, index, r){
8044         this.selections.remove(r);
8045     },
8046
8047     // private
8048     onRowUpdated : function(v, index, r){
8049         if(this.isSelected(r)){
8050             v.onRowSelect(index);
8051         }
8052     },
8053
8054     /**
8055      * Select records.
8056      * @param {Array} records The records to select
8057      * @param {Boolean} keepExisting (optional) True to keep existing selections
8058      */
8059     selectRecords : function(records, keepExisting){
8060         if(!keepExisting){
8061             this.clearSelections();
8062         }
8063         var ds = this.grid.ds;
8064         for(var i = 0, len = records.length; i < len; i++){
8065             this.selectRow(ds.indexOf(records[i]), true);
8066         }
8067     },
8068
8069     /**
8070      * Gets the number of selected rows.
8071      * @return {Number}
8072      */
8073     getCount : function(){
8074         return this.selections.length;
8075     },
8076
8077     /**
8078      * Selects the first row in the grid.
8079      */
8080     selectFirstRow : function(){
8081         this.selectRow(0);
8082     },
8083
8084     /**
8085      * Select the last row.
8086      * @param {Boolean} keepExisting (optional) True to keep existing selections
8087      */
8088     selectLastRow : function(keepExisting){
8089         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8090     },
8091
8092     /**
8093      * Selects the row immediately following the last selected row.
8094      * @param {Boolean} keepExisting (optional) True to keep existing selections
8095      */
8096     selectNext : function(keepExisting){
8097         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8098             this.selectRow(this.last+1, keepExisting);
8099             var view = this.grid.view ? this.grid.view : this.grid;
8100             view.focusRow(this.last);
8101         }
8102     },
8103
8104     /**
8105      * Selects the row that precedes the last selected row.
8106      * @param {Boolean} keepExisting (optional) True to keep existing selections
8107      */
8108     selectPrevious : function(keepExisting){
8109         if(this.last){
8110             this.selectRow(this.last-1, keepExisting);
8111             var view = this.grid.view ? this.grid.view : this.grid;
8112             view.focusRow(this.last);
8113         }
8114     },
8115
8116     /**
8117      * Returns the selected records
8118      * @return {Array} Array of selected records
8119      */
8120     getSelections : function(){
8121         return [].concat(this.selections.items);
8122     },
8123
8124     /**
8125      * Returns the first selected record.
8126      * @return {Record}
8127      */
8128     getSelected : function(){
8129         return this.selections.itemAt(0);
8130     },
8131
8132
8133     /**
8134      * Clears all selections.
8135      */
8136     clearSelections : function(fast){
8137         if(this.locked) {
8138             return;
8139         }
8140         if(fast !== true){
8141             var ds = this.grid.ds;
8142             var s = this.selections;
8143             s.each(function(r){
8144                 this.deselectRow(ds.indexOfId(r.id));
8145             }, this);
8146             s.clear();
8147         }else{
8148             this.selections.clear();
8149         }
8150         this.last = false;
8151     },
8152
8153
8154     /**
8155      * Selects all rows.
8156      */
8157     selectAll : function(){
8158         if(this.locked) {
8159             return;
8160         }
8161         this.selections.clear();
8162         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8163             this.selectRow(i, true);
8164         }
8165     },
8166
8167     /**
8168      * Returns True if there is a selection.
8169      * @return {Boolean}
8170      */
8171     hasSelection : function(){
8172         return this.selections.length > 0;
8173     },
8174
8175     /**
8176      * Returns True if the specified row is selected.
8177      * @param {Number/Record} record The record or index of the record to check
8178      * @return {Boolean}
8179      */
8180     isSelected : function(index){
8181         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8182         return (r && this.selections.key(r.id) ? true : false);
8183     },
8184
8185     /**
8186      * Returns True if the specified record id is selected.
8187      * @param {String} id The id of record to check
8188      * @return {Boolean}
8189      */
8190     isIdSelected : function(id){
8191         return (this.selections.key(id) ? true : false);
8192     },
8193
8194     // private
8195     handleMouseDown : function(e, t)
8196     {
8197         var view = this.grid.view ? this.grid.view : this.grid;
8198         var rowIndex;
8199         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8200             return;
8201         };
8202         if(e.shiftKey && this.last !== false){
8203             var last = this.last;
8204             this.selectRange(last, rowIndex, e.ctrlKey);
8205             this.last = last; // reset the last
8206             view.focusRow(rowIndex);
8207         }else{
8208             var isSelected = this.isSelected(rowIndex);
8209             if(e.button !== 0 && isSelected){
8210                 view.focusRow(rowIndex);
8211             }else if(e.ctrlKey && isSelected){
8212                 this.deselectRow(rowIndex);
8213             }else if(!isSelected){
8214                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8215                 view.focusRow(rowIndex);
8216             }
8217         }
8218         this.fireEvent("afterselectionchange", this);
8219     },
8220     // private
8221     handleDragableRowClick :  function(grid, rowIndex, e) 
8222     {
8223         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8224             this.selectRow(rowIndex, false);
8225             var view = this.grid.view ? this.grid.view : this.grid;
8226             view.focusRow(rowIndex);
8227              this.fireEvent("afterselectionchange", this);
8228         }
8229     },
8230     
8231     /**
8232      * Selects multiple rows.
8233      * @param {Array} rows Array of the indexes of the row to select
8234      * @param {Boolean} keepExisting (optional) True to keep existing selections
8235      */
8236     selectRows : function(rows, keepExisting){
8237         if(!keepExisting){
8238             this.clearSelections();
8239         }
8240         for(var i = 0, len = rows.length; i < len; i++){
8241             this.selectRow(rows[i], true);
8242         }
8243     },
8244
8245     /**
8246      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      * @param {Boolean} keepExisting (optional) True to retain existing selections
8250      */
8251     selectRange : function(startRow, endRow, keepExisting){
8252         if(this.locked) {
8253             return;
8254         }
8255         if(!keepExisting){
8256             this.clearSelections();
8257         }
8258         if(startRow <= endRow){
8259             for(var i = startRow; i <= endRow; i++){
8260                 this.selectRow(i, true);
8261             }
8262         }else{
8263             for(var i = startRow; i >= endRow; i--){
8264                 this.selectRow(i, true);
8265             }
8266         }
8267     },
8268
8269     /**
8270      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8271      * @param {Number} startRow The index of the first row in the range
8272      * @param {Number} endRow The index of the last row in the range
8273      */
8274     deselectRange : function(startRow, endRow, preventViewNotify){
8275         if(this.locked) {
8276             return;
8277         }
8278         for(var i = startRow; i <= endRow; i++){
8279             this.deselectRow(i, preventViewNotify);
8280         }
8281     },
8282
8283     /**
8284      * Selects a row.
8285      * @param {Number} row The index of the row to select
8286      * @param {Boolean} keepExisting (optional) True to keep existing selections
8287      */
8288     selectRow : function(index, keepExisting, preventViewNotify){
8289         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8290             return;
8291         }
8292         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8293             if(!keepExisting || this.singleSelect){
8294                 this.clearSelections();
8295             }
8296             var r = this.grid.ds.getAt(index);
8297             this.selections.add(r);
8298             this.last = this.lastActive = index;
8299             if(!preventViewNotify){
8300                 var view = this.grid.view ? this.grid.view : this.grid;
8301                 view.onRowSelect(index);
8302             }
8303             this.fireEvent("rowselect", this, index, r);
8304             this.fireEvent("selectionchange", this);
8305         }
8306     },
8307
8308     /**
8309      * Deselects a row.
8310      * @param {Number} row The index of the row to deselect
8311      */
8312     deselectRow : function(index, preventViewNotify){
8313         if(this.locked) {
8314             return;
8315         }
8316         if(this.last == index){
8317             this.last = false;
8318         }
8319         if(this.lastActive == index){
8320             this.lastActive = false;
8321         }
8322         var r = this.grid.ds.getAt(index);
8323         this.selections.remove(r);
8324         if(!preventViewNotify){
8325             var view = this.grid.view ? this.grid.view : this.grid;
8326             view.onRowDeselect(index);
8327         }
8328         this.fireEvent("rowdeselect", this, index);
8329         this.fireEvent("selectionchange", this);
8330     },
8331
8332     // private
8333     restoreLast : function(){
8334         if(this._last){
8335             this.last = this._last;
8336         }
8337     },
8338
8339     // private
8340     acceptsNav : function(row, col, cm){
8341         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8342     },
8343
8344     // private
8345     onEditorKey : function(field, e){
8346         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8347         if(k == e.TAB){
8348             e.stopEvent();
8349             ed.completeEdit();
8350             if(e.shiftKey){
8351                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8352             }else{
8353                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8354             }
8355         }else if(k == e.ENTER && !e.ctrlKey){
8356             e.stopEvent();
8357             ed.completeEdit();
8358             if(e.shiftKey){
8359                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8360             }else{
8361                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8362             }
8363         }else if(k == e.ESC){
8364             ed.cancelEdit();
8365         }
8366         if(newCell){
8367             g.startEditing(newCell[0], newCell[1]);
8368         }
8369     }
8370 });/*
8371  * Based on:
8372  * Ext JS Library 1.1.1
8373  * Copyright(c) 2006-2007, Ext JS, LLC.
8374  *
8375  * Originally Released Under LGPL - original licence link has changed is not relivant.
8376  *
8377  * Fork - LGPL
8378  * <script type="text/javascript">
8379  */
8380  
8381
8382 /**
8383  * @class Roo.grid.ColumnModel
8384  * @extends Roo.util.Observable
8385  * This is the default implementation of a ColumnModel used by the Grid. It defines
8386  * the columns in the grid.
8387  * <br>Usage:<br>
8388  <pre><code>
8389  var colModel = new Roo.grid.ColumnModel([
8390         {header: "Ticker", width: 60, sortable: true, locked: true},
8391         {header: "Company Name", width: 150, sortable: true},
8392         {header: "Market Cap.", width: 100, sortable: true},
8393         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8394         {header: "Employees", width: 100, sortable: true, resizable: false}
8395  ]);
8396  </code></pre>
8397  * <p>
8398  
8399  * The config options listed for this class are options which may appear in each
8400  * individual column definition.
8401  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8402  * @constructor
8403  * @param {Object} config An Array of column config objects. See this class's
8404  * config objects for details.
8405 */
8406 Roo.grid.ColumnModel = function(config){
8407         /**
8408      * The config passed into the constructor
8409      */
8410     this.config = []; //config;
8411     this.lookup = {};
8412
8413     // if no id, create one
8414     // if the column does not have a dataIndex mapping,
8415     // map it to the order it is in the config
8416     for(var i = 0, len = config.length; i < len; i++){
8417         this.addColumn(config[i]);
8418         
8419     }
8420
8421     /**
8422      * The width of columns which have no width specified (defaults to 100)
8423      * @type Number
8424      */
8425     this.defaultWidth = 100;
8426
8427     /**
8428      * Default sortable of columns which have no sortable specified (defaults to false)
8429      * @type Boolean
8430      */
8431     this.defaultSortable = false;
8432
8433     this.addEvents({
8434         /**
8435              * @event widthchange
8436              * Fires when the width of a column changes.
8437              * @param {ColumnModel} this
8438              * @param {Number} columnIndex The column index
8439              * @param {Number} newWidth The new width
8440              */
8441             "widthchange": true,
8442         /**
8443              * @event headerchange
8444              * Fires when the text of a header changes.
8445              * @param {ColumnModel} this
8446              * @param {Number} columnIndex The column index
8447              * @param {Number} newText The new header text
8448              */
8449             "headerchange": true,
8450         /**
8451              * @event hiddenchange
8452              * Fires when a column is hidden or "unhidden".
8453              * @param {ColumnModel} this
8454              * @param {Number} columnIndex The column index
8455              * @param {Boolean} hidden true if hidden, false otherwise
8456              */
8457             "hiddenchange": true,
8458             /**
8459          * @event columnmoved
8460          * Fires when a column is moved.
8461          * @param {ColumnModel} this
8462          * @param {Number} oldIndex
8463          * @param {Number} newIndex
8464          */
8465         "columnmoved" : true,
8466         /**
8467          * @event columlockchange
8468          * Fires when a column's locked state is changed
8469          * @param {ColumnModel} this
8470          * @param {Number} colIndex
8471          * @param {Boolean} locked true if locked
8472          */
8473         "columnlockchange" : true
8474     });
8475     Roo.grid.ColumnModel.superclass.constructor.call(this);
8476 };
8477 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8478     /**
8479      * @cfg {String} header [required] The header text to display in the Grid view.
8480      */
8481         /**
8482      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8483      */
8484         /**
8485      * @cfg {String} smHeader Header at Bootsrap Small width
8486      */
8487         /**
8488      * @cfg {String} mdHeader Header at Bootsrap Medium width
8489      */
8490         /**
8491      * @cfg {String} lgHeader Header at Bootsrap Large width
8492      */
8493         /**
8494      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8495      */
8496     /**
8497      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8498      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8499      * specified, the column's index is used as an index into the Record's data Array.
8500      */
8501     /**
8502      * @cfg {Number} width  The initial width in pixels of the column. Using this
8503      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8504      */
8505     /**
8506      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8507      * Defaults to the value of the {@link #defaultSortable} property.
8508      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8509      */
8510     /**
8511      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8512      */
8513     /**
8514      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8515      */
8516     /**
8517      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8518      */
8519     /**
8520      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8521      */
8522     /**
8523      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8524      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8525      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8526      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8527      */
8528        /**
8529      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8530      */
8531     /**
8532      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8533      */
8534     /**
8535      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8536      */
8537     /**
8538      * @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)
8539      */
8540     /**
8541      * @cfg {String} tooltip mouse over tooltip text
8542      */
8543     /**
8544      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8545      */
8546     /**
8547      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8548      */
8549     /**
8550      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8551      */
8552     /**
8553      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8554      */
8555         /**
8556      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8557      */
8558     /**
8559      * Returns the id of the column at the specified index.
8560      * @param {Number} index The column index
8561      * @return {String} the id
8562      */
8563     getColumnId : function(index){
8564         return this.config[index].id;
8565     },
8566
8567     /**
8568      * Returns the column for a specified id.
8569      * @param {String} id The column id
8570      * @return {Object} the column
8571      */
8572     getColumnById : function(id){
8573         return this.lookup[id];
8574     },
8575
8576     
8577     /**
8578      * Returns the column Object for a specified dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Object|Boolean} the column or false if not found
8581      */
8582     getColumnByDataIndex: function(dataIndex){
8583         var index = this.findColumnIndex(dataIndex);
8584         return index > -1 ? this.config[index] : false;
8585     },
8586     
8587     /**
8588      * Returns the index for a specified column id.
8589      * @param {String} id The column id
8590      * @return {Number} the index, or -1 if not found
8591      */
8592     getIndexById : function(id){
8593         for(var i = 0, len = this.config.length; i < len; i++){
8594             if(this.config[i].id == id){
8595                 return i;
8596             }
8597         }
8598         return -1;
8599     },
8600     
8601     /**
8602      * Returns the index for a specified column dataIndex.
8603      * @param {String} dataIndex The column dataIndex
8604      * @return {Number} the index, or -1 if not found
8605      */
8606     
8607     findColumnIndex : function(dataIndex){
8608         for(var i = 0, len = this.config.length; i < len; i++){
8609             if(this.config[i].dataIndex == dataIndex){
8610                 return i;
8611             }
8612         }
8613         return -1;
8614     },
8615     
8616     
8617     moveColumn : function(oldIndex, newIndex){
8618         var c = this.config[oldIndex];
8619         this.config.splice(oldIndex, 1);
8620         this.config.splice(newIndex, 0, c);
8621         this.dataMap = null;
8622         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8623     },
8624
8625     isLocked : function(colIndex){
8626         return this.config[colIndex].locked === true;
8627     },
8628
8629     setLocked : function(colIndex, value, suppressEvent){
8630         if(this.isLocked(colIndex) == value){
8631             return;
8632         }
8633         this.config[colIndex].locked = value;
8634         if(!suppressEvent){
8635             this.fireEvent("columnlockchange", this, colIndex, value);
8636         }
8637     },
8638
8639     getTotalLockedWidth : function(){
8640         var totalWidth = 0;
8641         for(var i = 0; i < this.config.length; i++){
8642             if(this.isLocked(i) && !this.isHidden(i)){
8643                 this.totalWidth += this.getColumnWidth(i);
8644             }
8645         }
8646         return totalWidth;
8647     },
8648
8649     getLockedCount : function(){
8650         for(var i = 0, len = this.config.length; i < len; i++){
8651             if(!this.isLocked(i)){
8652                 return i;
8653             }
8654         }
8655         
8656         return this.config.length;
8657     },
8658
8659     /**
8660      * Returns the number of columns.
8661      * @return {Number}
8662      */
8663     getColumnCount : function(visibleOnly){
8664         if(visibleOnly === true){
8665             var c = 0;
8666             for(var i = 0, len = this.config.length; i < len; i++){
8667                 if(!this.isHidden(i)){
8668                     c++;
8669                 }
8670             }
8671             return c;
8672         }
8673         return this.config.length;
8674     },
8675
8676     /**
8677      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8678      * @param {Function} fn
8679      * @param {Object} scope (optional)
8680      * @return {Array} result
8681      */
8682     getColumnsBy : function(fn, scope){
8683         var r = [];
8684         for(var i = 0, len = this.config.length; i < len; i++){
8685             var c = this.config[i];
8686             if(fn.call(scope||this, c, i) === true){
8687                 r[r.length] = c;
8688             }
8689         }
8690         return r;
8691     },
8692
8693     /**
8694      * Returns true if the specified column is sortable.
8695      * @param {Number} col The column index
8696      * @return {Boolean}
8697      */
8698     isSortable : function(col){
8699         if(typeof this.config[col].sortable == "undefined"){
8700             return this.defaultSortable;
8701         }
8702         return this.config[col].sortable;
8703     },
8704
8705     /**
8706      * Returns the rendering (formatting) function defined for the column.
8707      * @param {Number} col The column index.
8708      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8709      */
8710     getRenderer : function(col){
8711         if(!this.config[col].renderer){
8712             return Roo.grid.ColumnModel.defaultRenderer;
8713         }
8714         return this.config[col].renderer;
8715     },
8716
8717     /**
8718      * Sets the rendering (formatting) function for a column.
8719      * @param {Number} col The column index
8720      * @param {Function} fn The function to use to process the cell's raw data
8721      * to return HTML markup for the grid view. The render function is called with
8722      * the following parameters:<ul>
8723      * <li>Data value.</li>
8724      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8725      * <li>css A CSS style string to apply to the table cell.</li>
8726      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8727      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8728      * <li>Row index</li>
8729      * <li>Column index</li>
8730      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8731      */
8732     setRenderer : function(col, fn){
8733         this.config[col].renderer = fn;
8734     },
8735
8736     /**
8737      * Returns the width for the specified column.
8738      * @param {Number} col The column index
8739      * @param (optional) {String} gridSize bootstrap width size.
8740      * @return {Number}
8741      */
8742     getColumnWidth : function(col, gridSize)
8743         {
8744                 var cfg = this.config[col];
8745                 
8746                 if (typeof(gridSize) == 'undefined') {
8747                         return cfg.width * 1 || this.defaultWidth;
8748                 }
8749                 if (gridSize === false) { // if we set it..
8750                         return cfg.width || false;
8751                 }
8752                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8753                 
8754                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8755                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8756                                 continue;
8757                         }
8758                         return cfg[ sizes[i] ];
8759                 }
8760                 return 1;
8761                 
8762     },
8763
8764     /**
8765      * Sets the width for a column.
8766      * @param {Number} col The column index
8767      * @param {Number} width The new width
8768      */
8769     setColumnWidth : function(col, width, suppressEvent){
8770         this.config[col].width = width;
8771         this.totalWidth = null;
8772         if(!suppressEvent){
8773              this.fireEvent("widthchange", this, col, width);
8774         }
8775     },
8776
8777     /**
8778      * Returns the total width of all columns.
8779      * @param {Boolean} includeHidden True to include hidden column widths
8780      * @return {Number}
8781      */
8782     getTotalWidth : function(includeHidden){
8783         if(!this.totalWidth){
8784             this.totalWidth = 0;
8785             for(var i = 0, len = this.config.length; i < len; i++){
8786                 if(includeHidden || !this.isHidden(i)){
8787                     this.totalWidth += this.getColumnWidth(i);
8788                 }
8789             }
8790         }
8791         return this.totalWidth;
8792     },
8793
8794     /**
8795      * Returns the header for the specified column.
8796      * @param {Number} col The column index
8797      * @return {String}
8798      */
8799     getColumnHeader : function(col){
8800         return this.config[col].header;
8801     },
8802
8803     /**
8804      * Sets the header for a column.
8805      * @param {Number} col The column index
8806      * @param {String} header The new header
8807      */
8808     setColumnHeader : function(col, header){
8809         this.config[col].header = header;
8810         this.fireEvent("headerchange", this, col, header);
8811     },
8812
8813     /**
8814      * Returns the tooltip for the specified column.
8815      * @param {Number} col The column index
8816      * @return {String}
8817      */
8818     getColumnTooltip : function(col){
8819             return this.config[col].tooltip;
8820     },
8821     /**
8822      * Sets the tooltip for a column.
8823      * @param {Number} col The column index
8824      * @param {String} tooltip The new tooltip
8825      */
8826     setColumnTooltip : function(col, tooltip){
8827             this.config[col].tooltip = tooltip;
8828     },
8829
8830     /**
8831      * Returns the dataIndex for the specified column.
8832      * @param {Number} col The column index
8833      * @return {Number}
8834      */
8835     getDataIndex : function(col){
8836         return this.config[col].dataIndex;
8837     },
8838
8839     /**
8840      * Sets the dataIndex for a column.
8841      * @param {Number} col The column index
8842      * @param {Number} dataIndex The new dataIndex
8843      */
8844     setDataIndex : function(col, dataIndex){
8845         this.config[col].dataIndex = dataIndex;
8846     },
8847
8848     
8849     
8850     /**
8851      * Returns true if the cell is editable.
8852      * @param {Number} colIndex The column index
8853      * @param {Number} rowIndex The row index - this is nto actually used..?
8854      * @return {Boolean}
8855      */
8856     isCellEditable : function(colIndex, rowIndex){
8857         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8858     },
8859
8860     /**
8861      * Returns the editor defined for the cell/column.
8862      * return false or null to disable editing.
8863      * @param {Number} colIndex The column index
8864      * @param {Number} rowIndex The row index
8865      * @return {Object}
8866      */
8867     getCellEditor : function(colIndex, rowIndex){
8868         return this.config[colIndex].editor;
8869     },
8870
8871     /**
8872      * Sets if a column is editable.
8873      * @param {Number} col The column index
8874      * @param {Boolean} editable True if the column is editable
8875      */
8876     setEditable : function(col, editable){
8877         this.config[col].editable = editable;
8878     },
8879
8880
8881     /**
8882      * Returns true if the column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @return {Boolean}
8885      */
8886     isHidden : function(colIndex){
8887         return this.config[colIndex].hidden;
8888     },
8889
8890
8891     /**
8892      * Returns true if the column width cannot be changed
8893      */
8894     isFixed : function(colIndex){
8895         return this.config[colIndex].fixed;
8896     },
8897
8898     /**
8899      * Returns true if the column can be resized
8900      * @return {Boolean}
8901      */
8902     isResizable : function(colIndex){
8903         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8904     },
8905     /**
8906      * Sets if a column is hidden.
8907      * @param {Number} colIndex The column index
8908      * @param {Boolean} hidden True if the column is hidden
8909      */
8910     setHidden : function(colIndex, hidden){
8911         this.config[colIndex].hidden = hidden;
8912         this.totalWidth = null;
8913         this.fireEvent("hiddenchange", this, colIndex, hidden);
8914     },
8915
8916     /**
8917      * Sets the editor for a column.
8918      * @param {Number} col The column index
8919      * @param {Object} editor The editor object
8920      */
8921     setEditor : function(col, editor){
8922         this.config[col].editor = editor;
8923     },
8924     /**
8925      * Add a column (experimental...) - defaults to adding to the end..
8926      * @param {Object} config 
8927     */
8928     addColumn : function(c)
8929     {
8930     
8931         var i = this.config.length;
8932         this.config[i] = c;
8933         
8934         if(typeof c.dataIndex == "undefined"){
8935             c.dataIndex = i;
8936         }
8937         if(typeof c.renderer == "string"){
8938             c.renderer = Roo.util.Format[c.renderer];
8939         }
8940         if(typeof c.id == "undefined"){
8941             c.id = Roo.id();
8942         }
8943         if(c.editor && c.editor.xtype){
8944             c.editor  = Roo.factory(c.editor, Roo.grid);
8945         }
8946         if(c.editor && c.editor.isFormField){
8947             c.editor = new Roo.grid.GridEditor(c.editor);
8948         }
8949         this.lookup[c.id] = c;
8950     }
8951     
8952 });
8953
8954 Roo.grid.ColumnModel.defaultRenderer = function(value)
8955 {
8956     if(typeof value == "object") {
8957         return value;
8958     }
8959         if(typeof value == "string" && value.length < 1){
8960             return "&#160;";
8961         }
8962     
8963         return String.format("{0}", value);
8964 };
8965
8966 // Alias for backwards compatibility
8967 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8968 /*
8969  * Based on:
8970  * Ext JS Library 1.1.1
8971  * Copyright(c) 2006-2007, Ext JS, LLC.
8972  *
8973  * Originally Released Under LGPL - original licence link has changed is not relivant.
8974  *
8975  * Fork - LGPL
8976  * <script type="text/javascript">
8977  */
8978  
8979 /**
8980  * @class Roo.LoadMask
8981  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8982  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8983  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8984  * element's UpdateManager load indicator and will be destroyed after the initial load.
8985  * @constructor
8986  * Create a new LoadMask
8987  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8988  * @param {Object} config The config object
8989  */
8990 Roo.LoadMask = function(el, config){
8991     this.el = Roo.get(el);
8992     Roo.apply(this, config);
8993     if(this.store){
8994         this.store.on('beforeload', this.onBeforeLoad, this);
8995         this.store.on('load', this.onLoad, this);
8996         this.store.on('loadexception', this.onLoadException, this);
8997         this.removeMask = false;
8998     }else{
8999         var um = this.el.getUpdateManager();
9000         um.showLoadIndicator = false; // disable the default indicator
9001         um.on('beforeupdate', this.onBeforeLoad, this);
9002         um.on('update', this.onLoad, this);
9003         um.on('failure', this.onLoad, this);
9004         this.removeMask = true;
9005     }
9006 };
9007
9008 Roo.LoadMask.prototype = {
9009     /**
9010      * @cfg {Boolean} removeMask
9011      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9012      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9013      */
9014     removeMask : false,
9015     /**
9016      * @cfg {String} msg
9017      * The text to display in a centered loading message box (defaults to 'Loading...')
9018      */
9019     msg : 'Loading...',
9020     /**
9021      * @cfg {String} msgCls
9022      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9023      */
9024     msgCls : 'x-mask-loading',
9025
9026     /**
9027      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9028      * @type Boolean
9029      */
9030     disabled: false,
9031
9032     /**
9033      * Disables the mask to prevent it from being displayed
9034      */
9035     disable : function(){
9036        this.disabled = true;
9037     },
9038
9039     /**
9040      * Enables the mask so that it can be displayed
9041      */
9042     enable : function(){
9043         this.disabled = false;
9044     },
9045     
9046     onLoadException : function()
9047     {
9048         Roo.log(arguments);
9049         
9050         if (typeof(arguments[3]) != 'undefined') {
9051             Roo.MessageBox.alert("Error loading",arguments[3]);
9052         } 
9053         /*
9054         try {
9055             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9056                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9057             }   
9058         } catch(e) {
9059             
9060         }
9061         */
9062     
9063         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9064     },
9065     // private
9066     onLoad : function()
9067     {
9068         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9069     },
9070
9071     // private
9072     onBeforeLoad : function(){
9073         if(!this.disabled){
9074             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9075         }
9076     },
9077
9078     // private
9079     destroy : function(){
9080         if(this.store){
9081             this.store.un('beforeload', this.onBeforeLoad, this);
9082             this.store.un('load', this.onLoad, this);
9083             this.store.un('loadexception', this.onLoadException, this);
9084         }else{
9085             var um = this.el.getUpdateManager();
9086             um.un('beforeupdate', this.onBeforeLoad, this);
9087             um.un('update', this.onLoad, this);
9088             um.un('failure', this.onLoad, this);
9089         }
9090     }
9091 };/**
9092  * @class Roo.bootstrap.Table
9093  * @licence LGBL
9094  * @extends Roo.bootstrap.Component
9095  * @children Roo.bootstrap.TableBody
9096  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9097  * Similar to Roo.grid.Grid
9098  * <pre><code>
9099  var table = Roo.factory({
9100     xtype : 'Table',
9101     xns : Roo.bootstrap,
9102     autoSizeColumns: true,
9103     
9104     
9105     store : {
9106         xtype : 'Store',
9107         xns : Roo.data,
9108         remoteSort : true,
9109         sortInfo : { direction : 'ASC', field: 'name' },
9110         proxy : {
9111            xtype : 'HttpProxy',
9112            xns : Roo.data,
9113            method : 'GET',
9114            url : 'https://example.com/some.data.url.json'
9115         },
9116         reader : {
9117            xtype : 'JsonReader',
9118            xns : Roo.data,
9119            fields : [ 'id', 'name', whatever' ],
9120            id : 'id',
9121            root : 'data'
9122         }
9123     },
9124     cm : [
9125         {
9126             xtype : 'ColumnModel',
9127             xns : Roo.grid,
9128             align : 'center',
9129             cursor : 'pointer',
9130             dataIndex : 'is_in_group',
9131             header : "Name",
9132             sortable : true,
9133             renderer : function(v, x , r) {  
9134             
9135                 return String.format("{0}", v)
9136             }
9137             width : 3
9138         } // more columns..
9139     ],
9140     selModel : {
9141         xtype : 'RowSelectionModel',
9142         xns : Roo.bootstrap.Table
9143         // you can add listeners to catch selection change here....
9144     }
9145      
9146
9147  });
9148  // set any options
9149  grid.render(Roo.get("some-div"));
9150 </code></pre>
9151
9152 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9153
9154
9155
9156  *
9157  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9158  * @cfg {Roo.data.Store} store The data store to use
9159  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9160  * 
9161  * @cfg {String} cls table class
9162  *
9163  *
9164  * @cfg {string} empty_results  Text to display for no results 
9165  * @cfg {boolean} striped Should the rows be alternative striped
9166  * @cfg {boolean} bordered Add borders to the table
9167  * @cfg {boolean} hover Add hover highlighting
9168  * @cfg {boolean} condensed Format condensed
9169  * @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,
9170  *                also adds table-responsive (see bootstrap docs for details)
9171  * @cfg {Boolean} loadMask (true|false) default false
9172  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9173  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9174  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9175  * @cfg {Boolean} rowSelection (true|false) default false
9176  * @cfg {Boolean} cellSelection (true|false) default false
9177  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9178  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9179  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9180  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9181  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9182  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9183  *
9184  * 
9185  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9186  * 
9187  * @constructor
9188  * Create a new Table
9189  * @param {Object} config The config object
9190  */
9191
9192 Roo.bootstrap.Table = function(config)
9193 {
9194     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9195      
9196     // BC...
9197     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9198     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9199     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9200     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9201     
9202     this.view = this; // compat with grid.
9203     
9204     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9205     if (this.sm) {
9206         this.sm.grid = this;
9207         this.selModel = Roo.factory(this.sm, Roo.grid);
9208         this.sm = this.selModel;
9209         this.sm.xmodule = this.xmodule || false;
9210     }
9211     
9212     if (this.cm && typeof(this.cm.config) == 'undefined') {
9213         this.colModel = new Roo.grid.ColumnModel(this.cm);
9214         this.cm = this.colModel;
9215         this.cm.xmodule = this.xmodule || false;
9216     }
9217     if (this.store) {
9218         this.store= Roo.factory(this.store, Roo.data);
9219         this.ds = this.store;
9220         this.ds.xmodule = this.xmodule || false;
9221          
9222     }
9223     if (this.footer && this.store) {
9224         this.footer.dataSource = this.ds;
9225         this.footer = Roo.factory(this.footer);
9226     }
9227     
9228     /** @private */
9229     this.addEvents({
9230         /**
9231          * @event cellclick
9232          * Fires when a cell is clicked
9233          * @param {Roo.bootstrap.Table} this
9234          * @param {Roo.Element} el
9235          * @param {Number} rowIndex
9236          * @param {Number} columnIndex
9237          * @param {Roo.EventObject} e
9238          */
9239         "cellclick" : true,
9240         /**
9241          * @event celldblclick
9242          * Fires when a cell is double clicked
9243          * @param {Roo.bootstrap.Table} this
9244          * @param {Roo.Element} el
9245          * @param {Number} rowIndex
9246          * @param {Number} columnIndex
9247          * @param {Roo.EventObject} e
9248          */
9249         "celldblclick" : true,
9250         /**
9251          * @event rowclick
9252          * Fires when a row is clicked
9253          * @param {Roo.bootstrap.Table} this
9254          * @param {Roo.Element} el
9255          * @param {Number} rowIndex
9256          * @param {Roo.EventObject} e
9257          */
9258         "rowclick" : true,
9259         /**
9260          * @event rowdblclick
9261          * Fires when a row is double clicked
9262          * @param {Roo.bootstrap.Table} this
9263          * @param {Roo.Element} el
9264          * @param {Number} rowIndex
9265          * @param {Roo.EventObject} e
9266          */
9267         "rowdblclick" : true,
9268         /**
9269          * @event mouseover
9270          * Fires when a mouseover occur
9271          * @param {Roo.bootstrap.Table} this
9272          * @param {Roo.Element} el
9273          * @param {Number} rowIndex
9274          * @param {Number} columnIndex
9275          * @param {Roo.EventObject} e
9276          */
9277         "mouseover" : true,
9278         /**
9279          * @event mouseout
9280          * Fires when a mouseout occur
9281          * @param {Roo.bootstrap.Table} this
9282          * @param {Roo.Element} el
9283          * @param {Number} rowIndex
9284          * @param {Number} columnIndex
9285          * @param {Roo.EventObject} e
9286          */
9287         "mouseout" : true,
9288         /**
9289          * @event rowclass
9290          * Fires when a row is rendered, so you can change add a style to it.
9291          * @param {Roo.bootstrap.Table} this
9292          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9293          */
9294         'rowclass' : true,
9295           /**
9296          * @event rowsrendered
9297          * Fires when all the  rows have been rendered
9298          * @param {Roo.bootstrap.Table} this
9299          */
9300         'rowsrendered' : true,
9301         /**
9302          * @event contextmenu
9303          * The raw contextmenu event for the entire grid.
9304          * @param {Roo.EventObject} e
9305          */
9306         "contextmenu" : true,
9307         /**
9308          * @event rowcontextmenu
9309          * Fires when a row is right clicked
9310          * @param {Roo.bootstrap.Table} this
9311          * @param {Number} rowIndex
9312          * @param {Roo.EventObject} e
9313          */
9314         "rowcontextmenu" : true,
9315         /**
9316          * @event cellcontextmenu
9317          * Fires when a cell is right clicked
9318          * @param {Roo.bootstrap.Table} this
9319          * @param {Number} rowIndex
9320          * @param {Number} cellIndex
9321          * @param {Roo.EventObject} e
9322          */
9323          "cellcontextmenu" : true,
9324          /**
9325          * @event headercontextmenu
9326          * Fires when a header is right clicked
9327          * @param {Roo.bootstrap.Table} this
9328          * @param {Number} columnIndex
9329          * @param {Roo.EventObject} e
9330          */
9331         "headercontextmenu" : true,
9332         /**
9333          * @event mousedown
9334          * The raw mousedown event for the entire grid.
9335          * @param {Roo.EventObject} e
9336          */
9337         "mousedown" : true
9338         
9339     });
9340 };
9341
9342 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9343     
9344     cls: false,
9345     
9346     empty_results : '',
9347     striped : false,
9348     scrollBody : false,
9349     bordered: false,
9350     hover:  false,
9351     condensed : false,
9352     responsive : false,
9353     sm : false,
9354     cm : false,
9355     store : false,
9356     loadMask : false,
9357     footerShow : true,
9358     footerRow : false,
9359     headerShow : true,
9360     enableColumnResize: true,
9361     disableAutoSize: false,
9362   
9363     rowSelection : false,
9364     cellSelection : false,
9365     layout : false,
9366
9367     minColumnWidth : 50,
9368     
9369     // Roo.Element - the tbody
9370     bodyEl: false,  // <tbody> Roo.Element - thead element    
9371     headEl: false,  // <thead> Roo.Element - thead element
9372     resizeProxy : false, // proxy element for dragging?
9373
9374
9375     
9376     container: false, // used by gridpanel...
9377     
9378     lazyLoad : false,
9379     
9380     CSS : Roo.util.CSS,
9381     
9382     auto_hide_footer : false,
9383     
9384     view: false, // actually points to this..
9385     
9386     getAutoCreate : function()
9387     {
9388         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9389         
9390         cfg = {
9391             tag: 'table',
9392             cls : 'table', 
9393             cn : []
9394         };
9395         // this get's auto added by panel.Grid
9396         if (this.scrollBody) {
9397             cfg.cls += ' table-body-fixed';
9398         }    
9399         if (this.striped) {
9400             cfg.cls += ' table-striped';
9401         }
9402         
9403         if (this.hover) {
9404             cfg.cls += ' table-hover';
9405         }
9406         if (this.bordered) {
9407             cfg.cls += ' table-bordered';
9408         }
9409         if (this.condensed) {
9410             cfg.cls += ' table-condensed';
9411         }
9412         
9413         if (this.responsive) {
9414             cfg.cls += ' table-responsive';
9415         }
9416         
9417         if (this.cls) {
9418             cfg.cls+=  ' ' +this.cls;
9419         }
9420         
9421         
9422         
9423         if (this.layout) {
9424             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9425         }
9426         
9427         if(this.store || this.cm){
9428             if(this.headerShow){
9429                 cfg.cn.push(this.renderHeader());
9430             }
9431             
9432             cfg.cn.push(this.renderBody());
9433             
9434             if(this.footerShow || this.footerRow){
9435                 cfg.cn.push(this.renderFooter());
9436             }
9437
9438             // where does this come from?
9439             //cfg.cls+=  ' TableGrid';
9440         }
9441         
9442         return { cn : [ cfg ] };
9443     },
9444     
9445     initEvents : function()
9446     {   
9447         if(!this.store || !this.cm){
9448             return;
9449         }
9450         if (this.selModel) {
9451             this.selModel.initEvents();
9452         }
9453         
9454         
9455         //Roo.log('initEvents with ds!!!!');
9456         
9457         this.bodyEl = this.el.select('tbody', true).first();
9458         this.headEl = this.el.select('thead', true).first();
9459         this.mainFoot = this.el.select('tfoot', true).first();
9460         
9461         
9462         
9463         
9464         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9465             e.on('click', this.sort, this);
9466         }, this);
9467         
9468         
9469         // why is this done????? = it breaks dialogs??
9470         //this.parent().el.setStyle('position', 'relative');
9471         
9472         
9473         if (this.footer) {
9474             this.footer.parentId = this.id;
9475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9476             
9477             if(this.lazyLoad){
9478                 this.el.select('tfoot tr td').first().addClass('hide');
9479             }
9480         } 
9481         
9482         if(this.loadMask) {
9483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9484         }
9485         
9486         this.store.on('load', this.onLoad, this);
9487         this.store.on('beforeload', this.onBeforeLoad, this);
9488         this.store.on('update', this.onUpdate, this);
9489         this.store.on('add', this.onAdd, this);
9490         this.store.on("clear", this.clear, this);
9491         
9492         this.el.on("contextmenu", this.onContextMenu, this);
9493         
9494         
9495         this.cm.on("headerchange", this.onHeaderChange, this);
9496         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9497
9498  //?? does bodyEl get replaced on render?
9499         this.bodyEl.on("click", this.onClick, this);
9500         this.bodyEl.on("dblclick", this.onDblClick, this);        
9501         this.bodyEl.on('scroll', this.onBodyScroll, this);
9502
9503         // guessing mainbody will work - this relays usually caught by selmodel at present.
9504         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9505   
9506   
9507         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9508         
9509   
9510         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9511             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9512         }
9513         
9514         this.initCSS();
9515     },
9516     // Compatibility with grid - we implement all the view features at present.
9517     getView : function()
9518     {
9519         return this;
9520     },
9521     
9522     initCSS : function()
9523     {
9524         if(this.disableAutoSize) {
9525             return;
9526         }
9527         
9528         var cm = this.cm, styles = [];
9529         this.CSS.removeStyleSheet(this.id + '-cssrules');
9530         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9531         // we can honour xs/sm/md/xl  as widths...
9532         // we first have to decide what widht we are currently at...
9533         var sz = Roo.getGridSize();
9534         
9535         var total = 0;
9536         var last = -1;
9537         var cols = []; // visable cols.
9538         var total_abs = 0;
9539         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9540             var w = cm.getColumnWidth(i, false);
9541             if(cm.isHidden(i)){
9542                 cols.push( { rel : false, abs : 0 });
9543                 continue;
9544             }
9545             if (w !== false) {
9546                 cols.push( { rel : false, abs : w });
9547                 total_abs += w;
9548                 last = i; // not really..
9549                 continue;
9550             }
9551             var w = cm.getColumnWidth(i, sz);
9552             if (w > 0) {
9553                 last = i
9554             }
9555             total += w;
9556             cols.push( { rel : w, abs : false });
9557         }
9558         
9559         var avail = this.bodyEl.dom.clientWidth - total_abs;
9560         
9561         var unitWidth = Math.floor(avail / total);
9562         var rem = avail - (unitWidth * total);
9563         
9564         var hidden, width, pos = 0 , splithide , left;
9565         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9566             
9567             hidden = 'display:none;';
9568             left = '';
9569             width  = 'width:0px;';
9570             splithide = '';
9571             if(!cm.isHidden(i)){
9572                 hidden = '';
9573                 
9574                 
9575                 // we can honour xs/sm/md/xl ?
9576                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9577                 if (w===0) {
9578                     hidden = 'display:none;';
9579                 }
9580                 // width should return a small number...
9581                 if (i == last) {
9582                     w+=rem; // add the remaining with..
9583                 }
9584                 pos += w;
9585                 left = "left:" + (pos -4) + "px;";
9586                 width = "width:" + w+ "px;";
9587                 
9588             }
9589             if (this.responsive) {
9590                 width = '';
9591                 left = '';
9592                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9593                 splithide = 'display: none;';
9594             }
9595             
9596             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9597             if (this.headEl) {
9598                 if (i == last) {
9599                     splithide = 'display:none;';
9600                 }
9601                 
9602                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9603                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9604                             // this is the popover version..
9605                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9606                 );
9607             }
9608             
9609         }
9610         //Roo.log(styles.join(''));
9611         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9612         
9613     },
9614     
9615     
9616     
9617     onContextMenu : function(e, t)
9618     {
9619         this.processEvent("contextmenu", e);
9620     },
9621     
9622     processEvent : function(name, e)
9623     {
9624         if (name != 'touchstart' ) {
9625             this.fireEvent(name, e);    
9626         }
9627         
9628         var t = e.getTarget();
9629         
9630         var cell = Roo.get(t);
9631         
9632         if(!cell){
9633             return;
9634         }
9635         
9636         if(cell.findParent('tfoot', false, true)){
9637             return;
9638         }
9639         
9640         if(cell.findParent('thead', false, true)){
9641             
9642             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9643                 cell = Roo.get(t).findParent('th', false, true);
9644                 if (!cell) {
9645                     Roo.log("failed to find th in thead?");
9646                     Roo.log(e.getTarget());
9647                     return;
9648                 }
9649             }
9650             
9651             var cellIndex = cell.dom.cellIndex;
9652             
9653             var ename = name == 'touchstart' ? 'click' : name;
9654             this.fireEvent("header" + ename, this, cellIndex, e);
9655             
9656             return;
9657         }
9658         
9659         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9660             cell = Roo.get(t).findParent('td', false, true);
9661             if (!cell) {
9662                 Roo.log("failed to find th in tbody?");
9663                 Roo.log(e.getTarget());
9664                 return;
9665             }
9666         }
9667         
9668         var row = cell.findParent('tr', false, true);
9669         var cellIndex = cell.dom.cellIndex;
9670         var rowIndex = row.dom.rowIndex - 1;
9671         
9672         if(row !== false){
9673             
9674             this.fireEvent("row" + name, this, rowIndex, e);
9675             
9676             if(cell !== false){
9677             
9678                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9679             }
9680         }
9681         
9682     },
9683     
9684     onMouseover : function(e, el)
9685     {
9686         var cell = Roo.get(el);
9687         
9688         if(!cell){
9689             return;
9690         }
9691         
9692         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9693             cell = cell.findParent('td', false, true);
9694         }
9695         
9696         var row = cell.findParent('tr', false, true);
9697         var cellIndex = cell.dom.cellIndex;
9698         var rowIndex = row.dom.rowIndex - 1; // start from 0
9699         
9700         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9701         
9702     },
9703     
9704     onMouseout : function(e, el)
9705     {
9706         var cell = Roo.get(el);
9707         
9708         if(!cell){
9709             return;
9710         }
9711         
9712         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9713             cell = cell.findParent('td', false, true);
9714         }
9715         
9716         var row = cell.findParent('tr', false, true);
9717         var cellIndex = cell.dom.cellIndex;
9718         var rowIndex = row.dom.rowIndex - 1; // start from 0
9719         
9720         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9721         
9722     },
9723     
9724     onClick : function(e, el)
9725     {
9726         var cell = Roo.get(el);
9727         
9728         if(!cell || (!this.cellSelection && !this.rowSelection)){
9729             return;
9730         }
9731         
9732         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9733             cell = cell.findParent('td', false, true);
9734         }
9735         
9736         if(!cell || typeof(cell) == 'undefined'){
9737             return;
9738         }
9739         
9740         var row = cell.findParent('tr', false, true);
9741         
9742         if(!row || typeof(row) == 'undefined'){
9743             return;
9744         }
9745         
9746         var cellIndex = cell.dom.cellIndex;
9747         var rowIndex = this.getRowIndex(row);
9748         
9749         // why??? - should these not be based on SelectionModel?
9750         //if(this.cellSelection){
9751             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9752         //}
9753         
9754         //if(this.rowSelection){
9755             this.fireEvent('rowclick', this, row, rowIndex, e);
9756         //}
9757          
9758     },
9759         
9760     onDblClick : function(e,el)
9761     {
9762         var cell = Roo.get(el);
9763         
9764         if(!cell || (!this.cellSelection && !this.rowSelection)){
9765             return;
9766         }
9767         
9768         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9769             cell = cell.findParent('td', false, true);
9770         }
9771         
9772         if(!cell || typeof(cell) == 'undefined'){
9773             return;
9774         }
9775         
9776         var row = cell.findParent('tr', false, true);
9777         
9778         if(!row || typeof(row) == 'undefined'){
9779             return;
9780         }
9781         
9782         var cellIndex = cell.dom.cellIndex;
9783         var rowIndex = this.getRowIndex(row);
9784         
9785         if(this.cellSelection){
9786             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9787         }
9788         
9789         if(this.rowSelection){
9790             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9791         }
9792     },
9793     findRowIndex : function(el)
9794     {
9795         var cell = Roo.get(el);
9796         if(!cell) {
9797             return false;
9798         }
9799         var row = cell.findParent('tr', false, true);
9800         
9801         if(!row || typeof(row) == 'undefined'){
9802             return false;
9803         }
9804         return this.getRowIndex(row);
9805     },
9806     sort : function(e,el)
9807     {
9808         var col = Roo.get(el);
9809         
9810         if(!col.hasClass('sortable')){
9811             return;
9812         }
9813         
9814         var sort = col.attr('sort');
9815         var dir = 'ASC';
9816         
9817         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9818             dir = 'DESC';
9819         }
9820         
9821         this.store.sortInfo = {field : sort, direction : dir};
9822         
9823         if (this.footer) {
9824             Roo.log("calling footer first");
9825             this.footer.onClick('first');
9826         } else {
9827         
9828             this.store.load({ params : { start : 0 } });
9829         }
9830     },
9831     
9832     renderHeader : function()
9833     {
9834         var header = {
9835             tag: 'thead',
9836             cn : []
9837         };
9838         
9839         var cm = this.cm;
9840         this.totalWidth = 0;
9841         
9842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9843             
9844             var config = cm.config[i];
9845             
9846             var c = {
9847                 tag: 'th',
9848                 cls : 'x-hcol-' + i,
9849                 style : '',
9850                 
9851                 html: cm.getColumnHeader(i)
9852             };
9853             
9854             var tooltip = cm.getColumnTooltip(i);
9855             if (tooltip) {
9856                 c.tooltip = tooltip;
9857             }
9858             
9859             
9860             var hh = '';
9861             
9862             if(typeof(config.sortable) != 'undefined' && config.sortable){
9863                 c.cls += ' sortable';
9864                 c.html = '<i class="fa"></i>' + c.html;
9865             }
9866             
9867             // could use BS4 hidden-..-down 
9868             
9869             if(typeof(config.lgHeader) != 'undefined'){
9870                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9871             }
9872             
9873             if(typeof(config.mdHeader) != 'undefined'){
9874                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9875             }
9876             
9877             if(typeof(config.smHeader) != 'undefined'){
9878                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9879             }
9880             
9881             if(typeof(config.xsHeader) != 'undefined'){
9882                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9883             }
9884             
9885             if(hh.length){
9886                 c.html = hh;
9887             }
9888             
9889             if(typeof(config.tooltip) != 'undefined'){
9890                 c.tooltip = config.tooltip;
9891             }
9892             
9893             if(typeof(config.colspan) != 'undefined'){
9894                 c.colspan = config.colspan;
9895             }
9896             
9897             // hidden is handled by CSS now
9898             
9899             if(typeof(config.dataIndex) != 'undefined'){
9900                 c.sort = config.dataIndex;
9901             }
9902             
9903            
9904             
9905             if(typeof(config.align) != 'undefined' && config.align.length){
9906                 c.style += ' text-align:' + config.align + ';';
9907             }
9908             
9909             /* width is done in CSS
9910              *if(typeof(config.width) != 'undefined'){
9911                 c.style += ' width:' + config.width + 'px;';
9912                 this.totalWidth += config.width;
9913             } else {
9914                 this.totalWidth += 100; // assume minimum of 100 per column?
9915             }
9916             */
9917             
9918             if(typeof(config.cls) != 'undefined'){
9919                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9920             }
9921             // this is the bit that doesnt reall work at all...
9922             
9923             if (this.responsive) {
9924                  
9925             
9926                 ['xs','sm','md','lg'].map(function(size){
9927                     
9928                     if(typeof(config[size]) == 'undefined'){
9929                         return;
9930                     }
9931                      
9932                     if (!config[size]) { // 0 = hidden
9933                         // BS 4 '0' is treated as hide that column and below.
9934                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9935                         return;
9936                     }
9937                     
9938                     c.cls += ' col-' + size + '-' + config[size] + (
9939                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9940                     );
9941                     
9942                     
9943                 });
9944             }
9945             // at the end?
9946             
9947             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9948             
9949             
9950             
9951             
9952             header.cn.push(c)
9953         }
9954         
9955         return header;
9956     },
9957     
9958     renderBody : function()
9959     {
9960         var body = {
9961             tag: 'tbody',
9962             cn : [
9963                 {
9964                     tag: 'tr',
9965                     cn : [
9966                         {
9967                             tag : 'td',
9968                             colspan :  this.cm.getColumnCount()
9969                         }
9970                     ]
9971                 }
9972             ]
9973         };
9974         
9975         return body;
9976     },
9977     
9978     renderFooter : function()
9979     {
9980         var footer = {
9981             tag: 'tfoot',
9982             cn : [
9983                 {
9984                     tag: 'tr',
9985                     cn : [
9986                         {
9987                             tag : 'td',
9988                             colspan :  this.cm.getColumnCount()
9989                         }
9990                     ]
9991                 }
9992             ]
9993         };
9994         
9995         return footer;
9996     },
9997     
9998     onLoad : function()
9999     {
10000 //        Roo.log('ds onload');
10001         this.clear();
10002         
10003         var _this = this;
10004         var cm = this.cm;
10005         var ds = this.store;
10006         
10007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10008             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10009             if (_this.store.sortInfo) {
10010                     
10011                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10012                     e.select('i', true).addClass(['fa-arrow-up']);
10013                 }
10014                 
10015                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10016                     e.select('i', true).addClass(['fa-arrow-down']);
10017                 }
10018             }
10019         });
10020         
10021         var tbody =  this.bodyEl;
10022               
10023         if(ds.getCount() > 0){
10024             ds.data.each(function(d,rowIndex){
10025                 var row =  this.renderRow(cm, ds, rowIndex);
10026                 
10027                 tbody.createChild(row);
10028                 
10029                 var _this = this;
10030                 
10031                 if(row.cellObjects.length){
10032                     Roo.each(row.cellObjects, function(r){
10033                         _this.renderCellObject(r);
10034                     })
10035                 }
10036                 
10037             }, this);
10038         } else if (this.empty_results.length) {
10039             this.el.mask(this.empty_results, 'no-spinner');
10040         }
10041         
10042         var tfoot = this.el.select('tfoot', true).first();
10043         
10044         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10045             
10046             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10047             
10048             var total = this.ds.getTotalCount();
10049             
10050             if(this.footer.pageSize < total){
10051                 this.mainFoot.show();
10052             }
10053         }
10054
10055         if(!this.footerShow && this.footerRow) {
10056
10057             var tr = {
10058                 tag : 'tr',
10059                 cn : []
10060             };
10061
10062             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10063                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10064                 var td = {
10065                     tag: 'td',
10066                     cls : ' x-fcol-' + i,
10067                     html: footer
10068                 };
10069
10070                 tr.cn.push(td);
10071                 
10072             }
10073             
10074             tfoot.dom.innerHTML = '';
10075
10076             tfoot.createChild(tr);
10077         }
10078         
10079         Roo.each(this.el.select('tbody td', true).elements, function(e){
10080             e.on('mouseover', _this.onMouseover, _this);
10081         });
10082         
10083         Roo.each(this.el.select('tbody td', true).elements, function(e){
10084             e.on('mouseout', _this.onMouseout, _this);
10085         });
10086         this.fireEvent('rowsrendered', this);
10087         
10088         this.autoSize();
10089         
10090         this.initCSS(); /// resize cols
10091
10092         
10093     },
10094     
10095     
10096     onUpdate : function(ds,record)
10097     {
10098         this.refreshRow(record);
10099         this.autoSize();
10100     },
10101     
10102     onRemove : function(ds, record, index, isUpdate){
10103         if(isUpdate !== true){
10104             this.fireEvent("beforerowremoved", this, index, record);
10105         }
10106         var bt = this.bodyEl.dom;
10107         
10108         var rows = this.el.select('tbody > tr', true).elements;
10109         
10110         if(typeof(rows[index]) != 'undefined'){
10111             bt.removeChild(rows[index].dom);
10112         }
10113         
10114 //        if(bt.rows[index]){
10115 //            bt.removeChild(bt.rows[index]);
10116 //        }
10117         
10118         if(isUpdate !== true){
10119             //this.stripeRows(index);
10120             //this.syncRowHeights(index, index);
10121             //this.layout();
10122             this.fireEvent("rowremoved", this, index, record);
10123         }
10124     },
10125     
10126     onAdd : function(ds, records, rowIndex)
10127     {
10128         //Roo.log('on Add called');
10129         // - note this does not handle multiple adding very well..
10130         var bt = this.bodyEl.dom;
10131         for (var i =0 ; i < records.length;i++) {
10132             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10133             //Roo.log(records[i]);
10134             //Roo.log(this.store.getAt(rowIndex+i));
10135             this.insertRow(this.store, rowIndex + i, false);
10136             return;
10137         }
10138         
10139     },
10140     
10141     
10142     refreshRow : function(record){
10143         var ds = this.store, index;
10144         if(typeof record == 'number'){
10145             index = record;
10146             record = ds.getAt(index);
10147         }else{
10148             index = ds.indexOf(record);
10149             if (index < 0) {
10150                 return; // should not happen - but seems to 
10151             }
10152         }
10153         this.insertRow(ds, index, true);
10154         this.autoSize();
10155         this.onRemove(ds, record, index+1, true);
10156         this.autoSize();
10157         //this.syncRowHeights(index, index);
10158         //this.layout();
10159         this.fireEvent("rowupdated", this, index, record);
10160     },
10161     // private - called by RowSelection
10162     onRowSelect : function(rowIndex){
10163         var row = this.getRowDom(rowIndex);
10164         row.addClass(['bg-info','info']);
10165     },
10166     // private - called by RowSelection
10167     onRowDeselect : function(rowIndex)
10168     {
10169         if (rowIndex < 0) {
10170             return;
10171         }
10172         var row = this.getRowDom(rowIndex);
10173         row.removeClass(['bg-info','info']);
10174     },
10175       /**
10176      * Focuses the specified row.
10177      * @param {Number} row The row index
10178      */
10179     focusRow : function(row)
10180     {
10181         //Roo.log('GridView.focusRow');
10182         var x = this.bodyEl.dom.scrollLeft;
10183         this.focusCell(row, 0, false);
10184         this.bodyEl.dom.scrollLeft = x;
10185
10186     },
10187      /**
10188      * Focuses the specified cell.
10189      * @param {Number} row The row index
10190      * @param {Number} col The column index
10191      * @param {Boolean} hscroll false to disable horizontal scrolling
10192      */
10193     focusCell : function(row, col, hscroll)
10194     {
10195         //Roo.log('GridView.focusCell');
10196         var el = this.ensureVisible(row, col, hscroll);
10197         // not sure what focusEL achives = it's a <a> pos relative 
10198         //this.focusEl.alignTo(el, "tl-tl");
10199         //if(Roo.isGecko){
10200         //    this.focusEl.focus();
10201         //}else{
10202         //    this.focusEl.focus.defer(1, this.focusEl);
10203         //}
10204     },
10205     
10206      /**
10207      * Scrolls the specified cell into view
10208      * @param {Number} row The row index
10209      * @param {Number} col The column index
10210      * @param {Boolean} hscroll false to disable horizontal scrolling
10211      */
10212     ensureVisible : function(row, col, hscroll)
10213     {
10214         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10215         //return null; //disable for testing.
10216         if(typeof row != "number"){
10217             row = row.rowIndex;
10218         }
10219         if(row < 0 && row >= this.ds.getCount()){
10220             return  null;
10221         }
10222         col = (col !== undefined ? col : 0);
10223         var cm = this.cm;
10224         while(cm.isHidden(col)){
10225             col++;
10226         }
10227
10228         var el = this.getCellDom(row, col);
10229         if(!el){
10230             return null;
10231         }
10232         var c = this.bodyEl.dom;
10233
10234         var ctop = parseInt(el.offsetTop, 10);
10235         var cleft = parseInt(el.offsetLeft, 10);
10236         var cbot = ctop + el.offsetHeight;
10237         var cright = cleft + el.offsetWidth;
10238
10239         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10240         var ch = 0; //?? header is not withing the area?
10241         var stop = parseInt(c.scrollTop, 10);
10242         var sleft = parseInt(c.scrollLeft, 10);
10243         var sbot = stop + ch;
10244         var sright = sleft + c.clientWidth;
10245         /*
10246         Roo.log('GridView.ensureVisible:' +
10247                 ' ctop:' + ctop +
10248                 ' c.clientHeight:' + c.clientHeight +
10249                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10250                 ' stop:' + stop +
10251                 ' cbot:' + cbot +
10252                 ' sbot:' + sbot +
10253                 ' ch:' + ch  
10254                 );
10255         */
10256         if(ctop < stop){
10257             c.scrollTop = ctop;
10258             //Roo.log("set scrolltop to ctop DISABLE?");
10259         }else if(cbot > sbot){
10260             //Roo.log("set scrolltop to cbot-ch");
10261             c.scrollTop = cbot-ch;
10262         }
10263
10264         if(hscroll !== false){
10265             if(cleft < sleft){
10266                 c.scrollLeft = cleft;
10267             }else if(cright > sright){
10268                 c.scrollLeft = cright-c.clientWidth;
10269             }
10270         }
10271
10272         return el;
10273     },
10274     
10275     
10276     insertRow : function(dm, rowIndex, isUpdate){
10277         
10278         if(!isUpdate){
10279             this.fireEvent("beforerowsinserted", this, rowIndex);
10280         }
10281             //var s = this.getScrollState();
10282         var row = this.renderRow(this.cm, this.store, rowIndex);
10283         // insert before rowIndex..
10284         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10285         
10286         var _this = this;
10287                 
10288         if(row.cellObjects.length){
10289             Roo.each(row.cellObjects, function(r){
10290                 _this.renderCellObject(r);
10291             })
10292         }
10293             
10294         if(!isUpdate){
10295             this.fireEvent("rowsinserted", this, rowIndex);
10296             //this.syncRowHeights(firstRow, lastRow);
10297             //this.stripeRows(firstRow);
10298             //this.layout();
10299         }
10300         
10301     },
10302     
10303     
10304     getRowDom : function(rowIndex)
10305     {
10306         var rows = this.el.select('tbody > tr', true).elements;
10307         
10308         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10309         
10310     },
10311     getCellDom : function(rowIndex, colIndex)
10312     {
10313         var row = this.getRowDom(rowIndex);
10314         if (row === false) {
10315             return false;
10316         }
10317         var cols = row.select('td', true).elements;
10318         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10319         
10320     },
10321     
10322     // returns the object tree for a tr..
10323   
10324     
10325     renderRow : function(cm, ds, rowIndex) 
10326     {
10327         var d = ds.getAt(rowIndex);
10328         
10329         var row = {
10330             tag : 'tr',
10331             cls : 'x-row-' + rowIndex,
10332             cn : []
10333         };
10334             
10335         var cellObjects = [];
10336         
10337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10338             var config = cm.config[i];
10339             
10340             var renderer = cm.getRenderer(i);
10341             var value = '';
10342             var id = false;
10343             
10344             if(typeof(renderer) !== 'undefined'){
10345                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10346             }
10347             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10348             // and are rendered into the cells after the row is rendered - using the id for the element.
10349             
10350             if(typeof(value) === 'object'){
10351                 id = Roo.id();
10352                 cellObjects.push({
10353                     container : id,
10354                     cfg : value 
10355                 })
10356             }
10357             
10358             var rowcfg = {
10359                 record: d,
10360                 rowIndex : rowIndex,
10361                 colIndex : i,
10362                 rowClass : ''
10363             };
10364
10365             this.fireEvent('rowclass', this, rowcfg);
10366             
10367             var td = {
10368                 tag: 'td',
10369                 // this might end up displaying HTML?
10370                 // this is too messy... - better to only do it on columsn you know are going to be too long
10371                 //tooltip : (typeof(value) === 'object') ? '' : value,
10372                 cls : rowcfg.rowClass + ' x-col-' + i,
10373                 style: '',
10374                 html: (typeof(value) === 'object') ? '' : value
10375             };
10376             
10377             if (id) {
10378                 td.id = id;
10379             }
10380             
10381             if(typeof(config.colspan) != 'undefined'){
10382                 td.colspan = config.colspan;
10383             }
10384             
10385             
10386             
10387             if(typeof(config.align) != 'undefined' && config.align.length){
10388                 td.style += ' text-align:' + config.align + ';';
10389             }
10390             if(typeof(config.valign) != 'undefined' && config.valign.length){
10391                 td.style += ' vertical-align:' + config.valign + ';';
10392             }
10393             /*
10394             if(typeof(config.width) != 'undefined'){
10395                 td.style += ' width:' +  config.width + 'px;';
10396             }
10397             */
10398             
10399             if(typeof(config.cursor) != 'undefined'){
10400                 td.style += ' cursor:' +  config.cursor + ';';
10401             }
10402             
10403             if(typeof(config.cls) != 'undefined'){
10404                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10405             }
10406             if (this.responsive) {
10407                 ['xs','sm','md','lg'].map(function(size){
10408                     
10409                     if(typeof(config[size]) == 'undefined'){
10410                         return;
10411                     }
10412                     
10413                     
10414                       
10415                     if (!config[size]) { // 0 = hidden
10416                         // BS 4 '0' is treated as hide that column and below.
10417                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10418                         return;
10419                     }
10420                     
10421                     td.cls += ' col-' + size + '-' + config[size] + (
10422                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10423                     );
10424                      
10425     
10426                 });
10427             }
10428             row.cn.push(td);
10429            
10430         }
10431         
10432         row.cellObjects = cellObjects;
10433         
10434         return row;
10435           
10436     },
10437     
10438     
10439     
10440     onBeforeLoad : function()
10441     {
10442         this.el.unmask(); // if needed.
10443     },
10444      /**
10445      * Remove all rows
10446      */
10447     clear : function()
10448     {
10449         this.el.select('tbody', true).first().dom.innerHTML = '';
10450     },
10451     /**
10452      * Show or hide a row.
10453      * @param {Number} rowIndex to show or hide
10454      * @param {Boolean} state hide
10455      */
10456     setRowVisibility : function(rowIndex, state)
10457     {
10458         var bt = this.bodyEl.dom;
10459         
10460         var rows = this.el.select('tbody > tr', true).elements;
10461         
10462         if(typeof(rows[rowIndex]) == 'undefined'){
10463             return;
10464         }
10465         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10466         
10467     },
10468     
10469     
10470     getSelectionModel : function(){
10471         if(!this.selModel){
10472             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10473         }
10474         return this.selModel;
10475     },
10476     /*
10477      * Render the Roo.bootstrap object from renderder
10478      */
10479     renderCellObject : function(r)
10480     {
10481         var _this = this;
10482         
10483         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10484         
10485         var t = r.cfg.render(r.container);
10486         
10487         if(r.cfg.cn){
10488             Roo.each(r.cfg.cn, function(c){
10489                 var child = {
10490                     container: t.getChildContainer(),
10491                     cfg: c
10492                 };
10493                 _this.renderCellObject(child);
10494             })
10495         }
10496     },
10497     /**
10498      * get the Row Index from a dom element.
10499      * @param {Roo.Element} row The row to look for
10500      * @returns {Number} the row
10501      */
10502     getRowIndex : function(row)
10503     {
10504         var rowIndex = -1;
10505         
10506         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10507             if(el != row){
10508                 return;
10509             }
10510             
10511             rowIndex = index;
10512         });
10513         
10514         return rowIndex;
10515     },
10516     /**
10517      * get the header TH element for columnIndex
10518      * @param {Number} columnIndex
10519      * @returns {Roo.Element}
10520      */
10521     getHeaderIndex: function(colIndex)
10522     {
10523         var cols = this.headEl.select('th', true).elements;
10524         return cols[colIndex]; 
10525     },
10526     /**
10527      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10528      * @param {domElement} cell to look for
10529      * @returns {Number} the column
10530      */
10531     getCellIndex : function(cell)
10532     {
10533         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10534         if(id){
10535             return parseInt(id[1], 10);
10536         }
10537         return 0;
10538     },
10539      /**
10540      * Returns the grid's underlying element = used by panel.Grid
10541      * @return {Element} The element
10542      */
10543     getGridEl : function(){
10544         return this.el;
10545     },
10546      /**
10547      * Forces a resize - used by panel.Grid
10548      * @return {Element} The element
10549      */
10550     autoSize : function()
10551     {
10552         if(this.disableAutoSize) {
10553             return;
10554         }
10555         //var ctr = Roo.get(this.container.dom.parentElement);
10556         var ctr = Roo.get(this.el.dom);
10557         
10558         var thd = this.getGridEl().select('thead',true).first();
10559         var tbd = this.getGridEl().select('tbody', true).first();
10560         var tfd = this.getGridEl().select('tfoot', true).first();
10561         
10562         var cw = ctr.getWidth();
10563         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10564         
10565         if (tbd) {
10566             
10567             tbd.setWidth(ctr.getWidth());
10568             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10569             // this needs fixing for various usage - currently only hydra job advers I think..
10570             //tdb.setHeight(
10571             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10572             //); 
10573             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10574             cw -= barsize;
10575         }
10576         cw = Math.max(cw, this.totalWidth);
10577         this.getGridEl().select('tbody tr',true).setWidth(cw);
10578         this.initCSS();
10579         
10580         // resize 'expandable coloumn?
10581         
10582         return; // we doe not have a view in this design..
10583         
10584     },
10585     onBodyScroll: function()
10586     {
10587         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10588         if(this.headEl){
10589             this.headEl.setStyle({
10590                 'position' : 'relative',
10591                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10592             });
10593         }
10594         
10595         if(this.lazyLoad){
10596             
10597             var scrollHeight = this.bodyEl.dom.scrollHeight;
10598             
10599             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10600             
10601             var height = this.bodyEl.getHeight();
10602             
10603             if(scrollHeight - height == scrollTop) {
10604                 
10605                 var total = this.ds.getTotalCount();
10606                 
10607                 if(this.footer.cursor + this.footer.pageSize < total){
10608                     
10609                     this.footer.ds.load({
10610                         params : {
10611                             start : this.footer.cursor + this.footer.pageSize,
10612                             limit : this.footer.pageSize
10613                         },
10614                         add : true
10615                     });
10616                 }
10617             }
10618             
10619         }
10620     },
10621     onColumnSplitterMoved : function(i, diff)
10622     {
10623         this.userResized = true;
10624         
10625         var cm = this.colModel;
10626         
10627         var w = this.getHeaderIndex(i).getWidth() + diff;
10628         
10629         
10630         cm.setColumnWidth(i, w, true);
10631         this.initCSS();
10632         //var cid = cm.getColumnId(i); << not used in this version?
10633        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10634         
10635         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10636         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10637         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10638 */
10639         //this.updateSplitters();
10640         //this.layout(); << ??
10641         this.fireEvent("columnresize", i, w);
10642     },
10643     onHeaderChange : function()
10644     {
10645         var header = this.renderHeader();
10646         var table = this.el.select('table', true).first();
10647         
10648         this.headEl.remove();
10649         this.headEl = table.createChild(header, this.bodyEl, false);
10650         
10651         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10652             e.on('click', this.sort, this);
10653         }, this);
10654         
10655         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10656             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10657         }
10658         
10659     },
10660     
10661     onHiddenChange : function(colModel, colIndex, hidden)
10662     {
10663         /*
10664         this.cm.setHidden()
10665         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10666         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10667         
10668         this.CSS.updateRule(thSelector, "display", "");
10669         this.CSS.updateRule(tdSelector, "display", "");
10670         
10671         if(hidden){
10672             this.CSS.updateRule(thSelector, "display", "none");
10673             this.CSS.updateRule(tdSelector, "display", "none");
10674         }
10675         */
10676         // onload calls initCSS()
10677         this.onHeaderChange();
10678         this.onLoad();
10679     },
10680     
10681     setColumnWidth: function(col_index, width)
10682     {
10683         // width = "md-2 xs-2..."
10684         if(!this.colModel.config[col_index]) {
10685             return;
10686         }
10687         
10688         var w = width.split(" ");
10689         
10690         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10691         
10692         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10693         
10694         
10695         for(var j = 0; j < w.length; j++) {
10696             
10697             if(!w[j]) {
10698                 continue;
10699             }
10700             
10701             var size_cls = w[j].split("-");
10702             
10703             if(!Number.isInteger(size_cls[1] * 1)) {
10704                 continue;
10705             }
10706             
10707             if(!this.colModel.config[col_index][size_cls[0]]) {
10708                 continue;
10709             }
10710             
10711             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10712                 continue;
10713             }
10714             
10715             h_row[0].classList.replace(
10716                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10717                 "col-"+size_cls[0]+"-"+size_cls[1]
10718             );
10719             
10720             for(var i = 0; i < rows.length; i++) {
10721                 
10722                 var size_cls = w[j].split("-");
10723                 
10724                 if(!Number.isInteger(size_cls[1] * 1)) {
10725                     continue;
10726                 }
10727                 
10728                 if(!this.colModel.config[col_index][size_cls[0]]) {
10729                     continue;
10730                 }
10731                 
10732                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10733                     continue;
10734                 }
10735                 
10736                 rows[i].classList.replace(
10737                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10738                     "col-"+size_cls[0]+"-"+size_cls[1]
10739                 );
10740             }
10741             
10742             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10743         }
10744     }
10745 });
10746
10747 // currently only used to find the split on drag.. 
10748 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10749
10750 /**
10751  * @depricated
10752 */
10753 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10754 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10755 /*
10756  * - LGPL
10757  *
10758  * table cell
10759  * 
10760  */
10761
10762 /**
10763  * @class Roo.bootstrap.TableCell
10764  * @extends Roo.bootstrap.Component
10765  * @children Roo.bootstrap.Component
10766  * @parent Roo.bootstrap.TableRow
10767  * Bootstrap TableCell class
10768  * 
10769  * @cfg {String} html cell contain text
10770  * @cfg {String} cls cell class
10771  * @cfg {String} tag cell tag (td|th) default td
10772  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10773  * @cfg {String} align Aligns the content in a cell
10774  * @cfg {String} axis Categorizes cells
10775  * @cfg {String} bgcolor Specifies the background color of a cell
10776  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10777  * @cfg {Number} colspan Specifies the number of columns a cell should span
10778  * @cfg {String} headers Specifies one or more header cells a cell is related to
10779  * @cfg {Number} height Sets the height of a cell
10780  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10781  * @cfg {Number} rowspan Sets the number of rows a cell should span
10782  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10783  * @cfg {String} valign Vertical aligns the content in a cell
10784  * @cfg {Number} width Specifies the width of a cell
10785  * 
10786  * @constructor
10787  * Create a new TableCell
10788  * @param {Object} config The config object
10789  */
10790
10791 Roo.bootstrap.TableCell = function(config){
10792     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10793 };
10794
10795 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10796     
10797     html: false,
10798     cls: false,
10799     tag: false,
10800     abbr: false,
10801     align: false,
10802     axis: false,
10803     bgcolor: false,
10804     charoff: false,
10805     colspan: false,
10806     headers: false,
10807     height: false,
10808     nowrap: false,
10809     rowspan: false,
10810     scope: false,
10811     valign: false,
10812     width: false,
10813     
10814     
10815     getAutoCreate : function(){
10816         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10817         
10818         cfg = {
10819             tag: 'td'
10820         };
10821         
10822         if(this.tag){
10823             cfg.tag = this.tag;
10824         }
10825         
10826         if (this.html) {
10827             cfg.html=this.html
10828         }
10829         if (this.cls) {
10830             cfg.cls=this.cls
10831         }
10832         if (this.abbr) {
10833             cfg.abbr=this.abbr
10834         }
10835         if (this.align) {
10836             cfg.align=this.align
10837         }
10838         if (this.axis) {
10839             cfg.axis=this.axis
10840         }
10841         if (this.bgcolor) {
10842             cfg.bgcolor=this.bgcolor
10843         }
10844         if (this.charoff) {
10845             cfg.charoff=this.charoff
10846         }
10847         if (this.colspan) {
10848             cfg.colspan=this.colspan
10849         }
10850         if (this.headers) {
10851             cfg.headers=this.headers
10852         }
10853         if (this.height) {
10854             cfg.height=this.height
10855         }
10856         if (this.nowrap) {
10857             cfg.nowrap=this.nowrap
10858         }
10859         if (this.rowspan) {
10860             cfg.rowspan=this.rowspan
10861         }
10862         if (this.scope) {
10863             cfg.scope=this.scope
10864         }
10865         if (this.valign) {
10866             cfg.valign=this.valign
10867         }
10868         if (this.width) {
10869             cfg.width=this.width
10870         }
10871         
10872         
10873         return cfg;
10874     }
10875    
10876 });
10877
10878  
10879
10880  /*
10881  * - LGPL
10882  *
10883  * table row
10884  * 
10885  */
10886
10887 /**
10888  * @class Roo.bootstrap.TableRow
10889  * @extends Roo.bootstrap.Component
10890  * @children Roo.bootstrap.TableCell
10891  * @parent Roo.bootstrap.TableBody
10892  * Bootstrap TableRow class
10893  * @cfg {String} cls row class
10894  * @cfg {String} align Aligns the content in a table row
10895  * @cfg {String} bgcolor Specifies a background color for a table row
10896  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10897  * @cfg {String} valign Vertical aligns the content in a table row
10898  * 
10899  * @constructor
10900  * Create a new TableRow
10901  * @param {Object} config The config object
10902  */
10903
10904 Roo.bootstrap.TableRow = function(config){
10905     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10906 };
10907
10908 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10909     
10910     cls: false,
10911     align: false,
10912     bgcolor: false,
10913     charoff: false,
10914     valign: false,
10915     
10916     getAutoCreate : function(){
10917         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10918         
10919         cfg = {
10920             tag: 'tr'
10921         };
10922             
10923         if(this.cls){
10924             cfg.cls = this.cls;
10925         }
10926         if(this.align){
10927             cfg.align = this.align;
10928         }
10929         if(this.bgcolor){
10930             cfg.bgcolor = this.bgcolor;
10931         }
10932         if(this.charoff){
10933             cfg.charoff = this.charoff;
10934         }
10935         if(this.valign){
10936             cfg.valign = this.valign;
10937         }
10938         
10939         return cfg;
10940     }
10941    
10942 });
10943
10944  
10945
10946  /*
10947  * - LGPL
10948  *
10949  * table body
10950  * 
10951  */
10952
10953 /**
10954  * @class Roo.bootstrap.TableBody
10955  * @extends Roo.bootstrap.Component
10956  * @children Roo.bootstrap.TableRow
10957  * @parent Roo.bootstrap.Table
10958  * Bootstrap TableBody class
10959  * @cfg {String} cls element class
10960  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10961  * @cfg {String} align Aligns the content inside the element
10962  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10963  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10964  * 
10965  * @constructor
10966  * Create a new TableBody
10967  * @param {Object} config The config object
10968  */
10969
10970 Roo.bootstrap.TableBody = function(config){
10971     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10972 };
10973
10974 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10975     
10976     cls: false,
10977     tag: false,
10978     align: false,
10979     charoff: false,
10980     valign: false,
10981     
10982     getAutoCreate : function(){
10983         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10984         
10985         cfg = {
10986             tag: 'tbody'
10987         };
10988             
10989         if (this.cls) {
10990             cfg.cls=this.cls
10991         }
10992         if(this.tag){
10993             cfg.tag = this.tag;
10994         }
10995         
10996         if(this.align){
10997             cfg.align = this.align;
10998         }
10999         if(this.charoff){
11000             cfg.charoff = this.charoff;
11001         }
11002         if(this.valign){
11003             cfg.valign = this.valign;
11004         }
11005         
11006         return cfg;
11007     }
11008     
11009     
11010 //    initEvents : function()
11011 //    {
11012 //        
11013 //        if(!this.store){
11014 //            return;
11015 //        }
11016 //        
11017 //        this.store = Roo.factory(this.store, Roo.data);
11018 //        this.store.on('load', this.onLoad, this);
11019 //        
11020 //        this.store.load();
11021 //        
11022 //    },
11023 //    
11024 //    onLoad: function () 
11025 //    {   
11026 //        this.fireEvent('load', this);
11027 //    }
11028 //    
11029 //   
11030 });
11031
11032  
11033
11034  /*
11035  * Based on:
11036  * Ext JS Library 1.1.1
11037  * Copyright(c) 2006-2007, Ext JS, LLC.
11038  *
11039  * Originally Released Under LGPL - original licence link has changed is not relivant.
11040  *
11041  * Fork - LGPL
11042  * <script type="text/javascript">
11043  */
11044
11045 // as we use this in bootstrap.
11046 Roo.namespace('Roo.form');
11047  /**
11048  * @class Roo.form.Action
11049  * Internal Class used to handle form actions
11050  * @constructor
11051  * @param {Roo.form.BasicForm} el The form element or its id
11052  * @param {Object} config Configuration options
11053  */
11054
11055  
11056  
11057 // define the action interface
11058 Roo.form.Action = function(form, options){
11059     this.form = form;
11060     this.options = options || {};
11061 };
11062 /**
11063  * Client Validation Failed
11064  * @const 
11065  */
11066 Roo.form.Action.CLIENT_INVALID = 'client';
11067 /**
11068  * Server Validation Failed
11069  * @const 
11070  */
11071 Roo.form.Action.SERVER_INVALID = 'server';
11072  /**
11073  * Connect to Server Failed
11074  * @const 
11075  */
11076 Roo.form.Action.CONNECT_FAILURE = 'connect';
11077 /**
11078  * Reading Data from Server Failed
11079  * @const 
11080  */
11081 Roo.form.Action.LOAD_FAILURE = 'load';
11082
11083 Roo.form.Action.prototype = {
11084     type : 'default',
11085     failureType : undefined,
11086     response : undefined,
11087     result : undefined,
11088
11089     // interface method
11090     run : function(options){
11091
11092     },
11093
11094     // interface method
11095     success : function(response){
11096
11097     },
11098
11099     // interface method
11100     handleResponse : function(response){
11101
11102     },
11103
11104     // default connection failure
11105     failure : function(response){
11106         
11107         this.response = response;
11108         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11109         this.form.afterAction(this, false);
11110     },
11111
11112     processResponse : function(response){
11113         this.response = response;
11114         if(!response.responseText){
11115             return true;
11116         }
11117         this.result = this.handleResponse(response);
11118         return this.result;
11119     },
11120
11121     // utility functions used internally
11122     getUrl : function(appendParams){
11123         var url = this.options.url || this.form.url || this.form.el.dom.action;
11124         if(appendParams){
11125             var p = this.getParams();
11126             if(p){
11127                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11128             }
11129         }
11130         return url;
11131     },
11132
11133     getMethod : function(){
11134         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11135     },
11136
11137     getParams : function(){
11138         var bp = this.form.baseParams;
11139         var p = this.options.params;
11140         if(p){
11141             if(typeof p == "object"){
11142                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11143             }else if(typeof p == 'string' && bp){
11144                 p += '&' + Roo.urlEncode(bp);
11145             }
11146         }else if(bp){
11147             p = Roo.urlEncode(bp);
11148         }
11149         return p;
11150     },
11151
11152     createCallback : function(){
11153         return {
11154             success: this.success,
11155             failure: this.failure,
11156             scope: this,
11157             timeout: (this.form.timeout*1000),
11158             upload: this.form.fileUpload ? this.success : undefined
11159         };
11160     }
11161 };
11162
11163 Roo.form.Action.Submit = function(form, options){
11164     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11165 };
11166
11167 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11168     type : 'submit',
11169
11170     haveProgress : false,
11171     uploadComplete : false,
11172     
11173     // uploadProgress indicator.
11174     uploadProgress : function()
11175     {
11176         if (!this.form.progressUrl) {
11177             return;
11178         }
11179         
11180         if (!this.haveProgress) {
11181             Roo.MessageBox.progress("Uploading", "Uploading");
11182         }
11183         if (this.uploadComplete) {
11184            Roo.MessageBox.hide();
11185            return;
11186         }
11187         
11188         this.haveProgress = true;
11189    
11190         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11191         
11192         var c = new Roo.data.Connection();
11193         c.request({
11194             url : this.form.progressUrl,
11195             params: {
11196                 id : uid
11197             },
11198             method: 'GET',
11199             success : function(req){
11200                //console.log(data);
11201                 var rdata = false;
11202                 var edata;
11203                 try  {
11204                    rdata = Roo.decode(req.responseText)
11205                 } catch (e) {
11206                     Roo.log("Invalid data from server..");
11207                     Roo.log(edata);
11208                     return;
11209                 }
11210                 if (!rdata || !rdata.success) {
11211                     Roo.log(rdata);
11212                     Roo.MessageBox.alert(Roo.encode(rdata));
11213                     return;
11214                 }
11215                 var data = rdata.data;
11216                 
11217                 if (this.uploadComplete) {
11218                    Roo.MessageBox.hide();
11219                    return;
11220                 }
11221                    
11222                 if (data){
11223                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11224                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11225                     );
11226                 }
11227                 this.uploadProgress.defer(2000,this);
11228             },
11229        
11230             failure: function(data) {
11231                 Roo.log('progress url failed ');
11232                 Roo.log(data);
11233             },
11234             scope : this
11235         });
11236            
11237     },
11238     
11239     
11240     run : function()
11241     {
11242         // run get Values on the form, so it syncs any secondary forms.
11243         this.form.getValues();
11244         
11245         var o = this.options;
11246         var method = this.getMethod();
11247         var isPost = method == 'POST';
11248         if(o.clientValidation === false || this.form.isValid()){
11249             
11250             if (this.form.progressUrl) {
11251                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11252                     (new Date() * 1) + '' + Math.random());
11253                     
11254             } 
11255             
11256             
11257             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11258                 form:this.form.el.dom,
11259                 url:this.getUrl(!isPost),
11260                 method: method,
11261                 params:isPost ? this.getParams() : null,
11262                 isUpload: this.form.fileUpload,
11263                 formData : this.form.formData
11264             }));
11265             
11266             this.uploadProgress();
11267
11268         }else if (o.clientValidation !== false){ // client validation failed
11269             this.failureType = Roo.form.Action.CLIENT_INVALID;
11270             this.form.afterAction(this, false);
11271         }
11272     },
11273
11274     success : function(response)
11275     {
11276         this.uploadComplete= true;
11277         if (this.haveProgress) {
11278             Roo.MessageBox.hide();
11279         }
11280         
11281         
11282         var result = this.processResponse(response);
11283         if(result === true || result.success){
11284             this.form.afterAction(this, true);
11285             return;
11286         }
11287         if(result.errors){
11288             this.form.markInvalid(result.errors);
11289             this.failureType = Roo.form.Action.SERVER_INVALID;
11290         }
11291         this.form.afterAction(this, false);
11292     },
11293     failure : function(response)
11294     {
11295         this.uploadComplete= true;
11296         if (this.haveProgress) {
11297             Roo.MessageBox.hide();
11298         }
11299         
11300         this.response = response;
11301         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11302         this.form.afterAction(this, false);
11303     },
11304     
11305     handleResponse : function(response){
11306         if(this.form.errorReader){
11307             var rs = this.form.errorReader.read(response);
11308             var errors = [];
11309             if(rs.records){
11310                 for(var i = 0, len = rs.records.length; i < len; i++) {
11311                     var r = rs.records[i];
11312                     errors[i] = r.data;
11313                 }
11314             }
11315             if(errors.length < 1){
11316                 errors = null;
11317             }
11318             return {
11319                 success : rs.success,
11320                 errors : errors
11321             };
11322         }
11323         var ret = false;
11324         try {
11325             var rt = response.responseText;
11326             if (rt.match(/^\<!--\[CDATA\[/)) {
11327                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11328                 rt = rt.replace(/\]\]--\>$/,'');
11329             }
11330             
11331             ret = Roo.decode(rt);
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         
12554         var id = Roo.id();
12555         
12556         var cfg = {};
12557         
12558         if(this.inputType != 'hidden'){
12559             cfg.cls = 'form-group' //input-group
12560         }
12561         
12562         var input =  {
12563             tag: 'input',
12564             id : id,
12565             type : this.inputType,
12566             value : this.value,
12567             cls : 'form-control',
12568             placeholder : this.placeholder || '',
12569             autocomplete : this.autocomplete || 'new-password'
12570         };
12571         if (this.inputType == 'file') {
12572             input.style = 'overflow:hidden'; // why not in CSS?
12573         }
12574         
12575         if(this.capture.length){
12576             input.capture = this.capture;
12577         }
12578         
12579         if(this.accept.length){
12580             input.accept = this.accept + "/*";
12581         }
12582         
12583         if(this.align){
12584             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12585         }
12586         
12587         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12588             input.maxLength = this.maxLength;
12589         }
12590         
12591         if (this.disabled) {
12592             input.disabled=true;
12593         }
12594         
12595         if (this.readOnly) {
12596             input.readonly=true;
12597         }
12598         
12599         if (this.name) {
12600             input.name = this.name;
12601         }
12602         
12603         if (this.size) {
12604             input.cls += ' input-' + this.size;
12605         }
12606         
12607         var settings=this;
12608         ['xs','sm','md','lg'].map(function(size){
12609             if (settings[size]) {
12610                 cfg.cls += ' col-' + size + '-' + settings[size];
12611             }
12612         });
12613         
12614         var inputblock = input;
12615         
12616         var feedback = {
12617             tag: 'span',
12618             cls: 'glyphicon form-control-feedback'
12619         };
12620             
12621         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12622             
12623             inputblock = {
12624                 cls : 'has-feedback',
12625                 cn :  [
12626                     input,
12627                     feedback
12628                 ] 
12629             };  
12630         }
12631         
12632         if (this.before || this.after) {
12633             
12634             inputblock = {
12635                 cls : 'input-group',
12636                 cn :  [] 
12637             };
12638             
12639             if (this.before && typeof(this.before) == 'string') {
12640                 
12641                 inputblock.cn.push({
12642                     tag :'span',
12643                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12644                     html : this.before
12645                 });
12646             }
12647             if (this.before && typeof(this.before) == 'object') {
12648                 this.before = Roo.factory(this.before);
12649                 
12650                 inputblock.cn.push({
12651                     tag :'span',
12652                     cls : 'roo-input-before input-group-prepend   input-group-' +
12653                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12654                 });
12655             }
12656             
12657             inputblock.cn.push(input);
12658             
12659             if (this.after && typeof(this.after) == 'string') {
12660                 inputblock.cn.push({
12661                     tag :'span',
12662                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12663                     html : this.after
12664                 });
12665             }
12666             if (this.after && typeof(this.after) == 'object') {
12667                 this.after = Roo.factory(this.after);
12668                 
12669                 inputblock.cn.push({
12670                     tag :'span',
12671                     cls : 'roo-input-after input-group-append  input-group-' +
12672                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12673                 });
12674             }
12675             
12676             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12677                 inputblock.cls += ' has-feedback';
12678                 inputblock.cn.push(feedback);
12679             }
12680         };
12681         
12682         
12683         
12684         cfg = this.getAutoCreateLabel( cfg, inputblock );
12685         
12686        
12687          
12688         
12689         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12690            cfg.cls += ' navbar-form';
12691         }
12692         
12693         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12694             // on BS4 we do this only if not form 
12695             cfg.cls += ' navbar-form';
12696             cfg.tag = 'li';
12697         }
12698         
12699         return cfg;
12700         
12701     },
12702     /**
12703      * autocreate the label - also used by textara... ?? and others?
12704      */
12705     getAutoCreateLabel : function( cfg, inputblock )
12706     {
12707         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12708        
12709         var indicator = {
12710             tag : 'i',
12711             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12712             tooltip : 'This field is required'
12713         };
12714         if (this.allowBlank ) {
12715             indicator.style = this.allowBlank ? ' display:none' : '';
12716         }
12717         if (align ==='left' && this.fieldLabel.length) {
12718             
12719             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12720             
12721             cfg.cn = [
12722                 indicator,
12723                 {
12724                     tag: 'label',
12725                     'for' :  id,
12726                     cls : 'control-label col-form-label',
12727                     html : this.fieldLabel
12728
12729                 },
12730                 {
12731                     cls : "", 
12732                     cn: [
12733                         inputblock
12734                     ]
12735                 }
12736             ];
12737             
12738             var labelCfg = cfg.cn[1];
12739             var contentCfg = cfg.cn[2];
12740             
12741             if(this.indicatorpos == 'right'){
12742                 cfg.cn = [
12743                     {
12744                         tag: 'label',
12745                         'for' :  id,
12746                         cls : 'control-label col-form-label',
12747                         cn : [
12748                             {
12749                                 tag : 'span',
12750                                 html : this.fieldLabel
12751                             },
12752                             indicator
12753                         ]
12754                     },
12755                     {
12756                         cls : "",
12757                         cn: [
12758                             inputblock
12759                         ]
12760                     }
12761
12762                 ];
12763                 
12764                 labelCfg = cfg.cn[0];
12765                 contentCfg = cfg.cn[1];
12766             
12767             }
12768             
12769             if(this.labelWidth > 12){
12770                 labelCfg.style = "width: " + this.labelWidth + 'px';
12771             }
12772             
12773             if(this.labelWidth < 13 && this.labelmd == 0){
12774                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12775             }
12776             
12777             if(this.labellg > 0){
12778                 labelCfg.cls += ' col-lg-' + this.labellg;
12779                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12780             }
12781             
12782             if(this.labelmd > 0){
12783                 labelCfg.cls += ' col-md-' + this.labelmd;
12784                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12785             }
12786             
12787             if(this.labelsm > 0){
12788                 labelCfg.cls += ' col-sm-' + this.labelsm;
12789                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12790             }
12791             
12792             if(this.labelxs > 0){
12793                 labelCfg.cls += ' col-xs-' + this.labelxs;
12794                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12795             }
12796             
12797             
12798         } else if ( this.fieldLabel.length) {
12799                 
12800             
12801             
12802             cfg.cn = [
12803                 {
12804                     tag : 'i',
12805                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12806                     tooltip : 'This field is required',
12807                     style : this.allowBlank ? ' display:none' : '' 
12808                 },
12809                 {
12810                     tag: 'label',
12811                    //cls : 'input-group-addon',
12812                     html : this.fieldLabel
12813
12814                 },
12815
12816                inputblock
12817
12818            ];
12819            
12820            if(this.indicatorpos == 'right'){
12821        
12822                 cfg.cn = [
12823                     {
12824                         tag: 'label',
12825                        //cls : 'input-group-addon',
12826                         html : this.fieldLabel
12827
12828                     },
12829                     {
12830                         tag : 'i',
12831                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12832                         tooltip : 'This field is required',
12833                         style : this.allowBlank ? ' display:none' : '' 
12834                     },
12835
12836                    inputblock
12837
12838                ];
12839
12840             }
12841
12842         } else {
12843             
12844             cfg.cn = [
12845
12846                     inputblock
12847
12848             ];
12849                 
12850                 
12851         };
12852         return cfg;
12853     },
12854     
12855     
12856     /**
12857      * return the real input element.
12858      */
12859     inputEl: function ()
12860     {
12861         return this.el.select('input.form-control',true).first();
12862     },
12863     
12864     tooltipEl : function()
12865     {
12866         return this.inputEl();
12867     },
12868     
12869     indicatorEl : function()
12870     {
12871         if (Roo.bootstrap.version == 4) {
12872             return false; // not enabled in v4 yet.
12873         }
12874         
12875         var indicator = this.el.select('i.roo-required-indicator',true).first();
12876         
12877         if(!indicator){
12878             return false;
12879         }
12880         
12881         return indicator;
12882         
12883     },
12884     
12885     setDisabled : function(v)
12886     {
12887         var i  = this.inputEl().dom;
12888         if (!v) {
12889             i.removeAttribute('disabled');
12890             return;
12891             
12892         }
12893         i.setAttribute('disabled','true');
12894     },
12895     initEvents : function()
12896     {
12897           
12898         this.inputEl().on("keydown" , this.fireKey,  this);
12899         this.inputEl().on("focus", this.onFocus,  this);
12900         this.inputEl().on("blur", this.onBlur,  this);
12901         
12902         this.inputEl().relayEvent('keyup', this);
12903         this.inputEl().relayEvent('paste', this);
12904         
12905         this.indicator = this.indicatorEl();
12906         
12907         if(this.indicator){
12908             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12909         }
12910  
12911         // reference to original value for reset
12912         this.originalValue = this.getValue();
12913         //Roo.form.TextField.superclass.initEvents.call(this);
12914         if(this.validationEvent == 'keyup'){
12915             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12916             this.inputEl().on('keyup', this.filterValidation, this);
12917         }
12918         else if(this.validationEvent !== false){
12919             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12920         }
12921         
12922         if(this.selectOnFocus){
12923             this.on("focus", this.preFocus, this);
12924             
12925         }
12926         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12927             this.inputEl().on("keypress", this.filterKeys, this);
12928         } else {
12929             this.inputEl().relayEvent('keypress', this);
12930         }
12931        /* if(this.grow){
12932             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12933             this.el.on("click", this.autoSize,  this);
12934         }
12935         */
12936         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12937             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12938         }
12939         
12940         if (typeof(this.before) == 'object') {
12941             this.before.render(this.el.select('.roo-input-before',true).first());
12942         }
12943         if (typeof(this.after) == 'object') {
12944             this.after.render(this.el.select('.roo-input-after',true).first());
12945         }
12946         
12947         this.inputEl().on('change', this.onChange, this);
12948         
12949     },
12950     filterValidation : function(e){
12951         if(!e.isNavKeyPress()){
12952             this.validationTask.delay(this.validationDelay);
12953         }
12954     },
12955      /**
12956      * Validates the field value
12957      * @return {Boolean} True if the value is valid, else false
12958      */
12959     validate : function(){
12960         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12961         if(this.disabled || this.validateValue(this.getRawValue())){
12962             this.markValid();
12963             return true;
12964         }
12965         
12966         this.markInvalid();
12967         return false;
12968     },
12969     
12970     
12971     /**
12972      * Validates a value according to the field's validation rules and marks the field as invalid
12973      * if the validation fails
12974      * @param {Mixed} value The value to validate
12975      * @return {Boolean} True if the value is valid, else false
12976      */
12977     validateValue : function(value)
12978     {
12979         if(this.getVisibilityEl().hasClass('hidden')){
12980             return true;
12981         }
12982         
12983         if(value.length < 1)  { // if it's blank
12984             if(this.allowBlank){
12985                 return true;
12986             }
12987             return false;
12988         }
12989         
12990         if(value.length < this.minLength){
12991             return false;
12992         }
12993         if(value.length > this.maxLength){
12994             return false;
12995         }
12996         if(this.vtype){
12997             var vt = Roo.form.VTypes;
12998             if(!vt[this.vtype](value, this)){
12999                 return false;
13000             }
13001         }
13002         if(typeof this.validator == "function"){
13003             var msg = this.validator(value);
13004             if (typeof(msg) == 'string') {
13005                 this.invalidText = msg;
13006             }
13007             if(msg !== true){
13008                 return false;
13009             }
13010         }
13011         
13012         if(this.regex && !this.regex.test(value)){
13013             return false;
13014         }
13015         
13016         return true;
13017     },
13018     
13019      // private
13020     fireKey : function(e){
13021         //Roo.log('field ' + e.getKey());
13022         if(e.isNavKeyPress()){
13023             this.fireEvent("specialkey", this, e);
13024         }
13025     },
13026     focus : function (selectText){
13027         if(this.rendered){
13028             this.inputEl().focus();
13029             if(selectText === true){
13030                 this.inputEl().dom.select();
13031             }
13032         }
13033         return this;
13034     } ,
13035     
13036     onFocus : function(){
13037         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13038            // this.el.addClass(this.focusClass);
13039         }
13040         if(!this.hasFocus){
13041             this.hasFocus = true;
13042             this.startValue = this.getValue();
13043             this.fireEvent("focus", this);
13044         }
13045     },
13046     
13047     beforeBlur : Roo.emptyFn,
13048
13049     
13050     // private
13051     onBlur : function(){
13052         this.beforeBlur();
13053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13054             //this.el.removeClass(this.focusClass);
13055         }
13056         this.hasFocus = false;
13057         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13058             this.validate();
13059         }
13060         var v = this.getValue();
13061         if(String(v) !== String(this.startValue)){
13062             this.fireEvent('change', this, v, this.startValue);
13063         }
13064         this.fireEvent("blur", this);
13065     },
13066     
13067     onChange : function(e)
13068     {
13069         var v = this.getValue();
13070         if(String(v) !== String(this.startValue)){
13071             this.fireEvent('change', this, v, this.startValue);
13072         }
13073         
13074     },
13075     
13076     /**
13077      * Resets the current field value to the originally loaded value and clears any validation messages
13078      */
13079     reset : function(){
13080         this.setValue(this.originalValue);
13081         this.validate();
13082     },
13083      /**
13084      * Returns the name of the field
13085      * @return {Mixed} name The name field
13086      */
13087     getName: function(){
13088         return this.name;
13089     },
13090      /**
13091      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13092      * @return {Mixed} value The field value
13093      */
13094     getValue : function(){
13095         var v = this.inputEl().getValue();
13096         return v;
13097     },
13098     /**
13099      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13100      * @return {Mixed} value The field value
13101      */
13102     getRawValue : function(){
13103         var v = this.inputEl().getValue();
13104         
13105         return v;
13106     },
13107     
13108     /**
13109      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13110      * @param {Mixed} value The value to set
13111      */
13112     setRawValue : function(v){
13113         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13114     },
13115     
13116     selectText : function(start, end){
13117         var v = this.getRawValue();
13118         if(v.length > 0){
13119             start = start === undefined ? 0 : start;
13120             end = end === undefined ? v.length : end;
13121             var d = this.inputEl().dom;
13122             if(d.setSelectionRange){
13123                 d.setSelectionRange(start, end);
13124             }else if(d.createTextRange){
13125                 var range = d.createTextRange();
13126                 range.moveStart("character", start);
13127                 range.moveEnd("character", v.length-end);
13128                 range.select();
13129             }
13130         }
13131     },
13132     
13133     /**
13134      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13135      * @param {Mixed} value The value to set
13136      */
13137     setValue : function(v){
13138         this.value = v;
13139         if(this.rendered){
13140             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13141             this.validate();
13142         }
13143     },
13144     
13145     /*
13146     processValue : function(value){
13147         if(this.stripCharsRe){
13148             var newValue = value.replace(this.stripCharsRe, '');
13149             if(newValue !== value){
13150                 this.setRawValue(newValue);
13151                 return newValue;
13152             }
13153         }
13154         return value;
13155     },
13156   */
13157     preFocus : function(){
13158         
13159         if(this.selectOnFocus){
13160             this.inputEl().dom.select();
13161         }
13162     },
13163     filterKeys : function(e){
13164         var k = e.getKey();
13165         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13166             return;
13167         }
13168         var c = e.getCharCode(), cc = String.fromCharCode(c);
13169         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13170             return;
13171         }
13172         if(!this.maskRe.test(cc)){
13173             e.stopEvent();
13174         }
13175     },
13176      /**
13177      * Clear any invalid styles/messages for this field
13178      */
13179     clearInvalid : function(){
13180         
13181         if(!this.el || this.preventMark){ // not rendered
13182             return;
13183         }
13184         
13185         
13186         this.el.removeClass([this.invalidClass, 'is-invalid']);
13187         
13188         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13189             
13190             var feedback = this.el.select('.form-control-feedback', true).first();
13191             
13192             if(feedback){
13193                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13194             }
13195             
13196         }
13197         
13198         if(this.indicator){
13199             this.indicator.removeClass('visible');
13200             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13201         }
13202         
13203         this.fireEvent('valid', this);
13204     },
13205     
13206      /**
13207      * Mark this field as valid
13208      */
13209     markValid : function()
13210     {
13211         if(!this.el  || this.preventMark){ // not rendered...
13212             return;
13213         }
13214         
13215         this.el.removeClass([this.invalidClass, this.validClass]);
13216         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217
13218         var feedback = this.el.select('.form-control-feedback', true).first();
13219             
13220         if(feedback){
13221             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13222         }
13223         
13224         if(this.indicator){
13225             this.indicator.removeClass('visible');
13226             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13227         }
13228         
13229         if(this.disabled){
13230             return;
13231         }
13232         
13233            
13234         if(this.allowBlank && !this.getRawValue().length){
13235             return;
13236         }
13237         if (Roo.bootstrap.version == 3) {
13238             this.el.addClass(this.validClass);
13239         } else {
13240             this.inputEl().addClass('is-valid');
13241         }
13242
13243         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13244             
13245             var feedback = this.el.select('.form-control-feedback', true).first();
13246             
13247             if(feedback){
13248                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13249                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13250             }
13251             
13252         }
13253         
13254         this.fireEvent('valid', this);
13255     },
13256     
13257      /**
13258      * Mark this field as invalid
13259      * @param {String} msg The validation message
13260      */
13261     markInvalid : function(msg)
13262     {
13263         if(!this.el  || this.preventMark){ // not rendered
13264             return;
13265         }
13266         
13267         this.el.removeClass([this.invalidClass, this.validClass]);
13268         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13269         
13270         var feedback = this.el.select('.form-control-feedback', true).first();
13271             
13272         if(feedback){
13273             this.el.select('.form-control-feedback', true).first().removeClass(
13274                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13275         }
13276
13277         if(this.disabled){
13278             return;
13279         }
13280         
13281         if(this.allowBlank && !this.getRawValue().length){
13282             return;
13283         }
13284         
13285         if(this.indicator){
13286             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13287             this.indicator.addClass('visible');
13288         }
13289         if (Roo.bootstrap.version == 3) {
13290             this.el.addClass(this.invalidClass);
13291         } else {
13292             this.inputEl().addClass('is-invalid');
13293         }
13294         
13295         
13296         
13297         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13298             
13299             var feedback = this.el.select('.form-control-feedback', true).first();
13300             
13301             if(feedback){
13302                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13303                 
13304                 if(this.getValue().length || this.forceFeedback){
13305                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13306                 }
13307                 
13308             }
13309             
13310         }
13311         
13312         this.fireEvent('invalid', this, msg);
13313     },
13314     // private
13315     SafariOnKeyDown : function(event)
13316     {
13317         // this is a workaround for a password hang bug on chrome/ webkit.
13318         if (this.inputEl().dom.type != 'password') {
13319             return;
13320         }
13321         
13322         var isSelectAll = false;
13323         
13324         if(this.inputEl().dom.selectionEnd > 0){
13325             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13326         }
13327         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13328             event.preventDefault();
13329             this.setValue('');
13330             return;
13331         }
13332         
13333         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13334             
13335             event.preventDefault();
13336             // this is very hacky as keydown always get's upper case.
13337             //
13338             var cc = String.fromCharCode(event.getCharCode());
13339             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13340             
13341         }
13342     },
13343     adjustWidth : function(tag, w){
13344         tag = tag.toLowerCase();
13345         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13346             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13347                 if(tag == 'input'){
13348                     return w + 2;
13349                 }
13350                 if(tag == 'textarea'){
13351                     return w-2;
13352                 }
13353             }else if(Roo.isOpera){
13354                 if(tag == 'input'){
13355                     return w + 2;
13356                 }
13357                 if(tag == 'textarea'){
13358                     return w-2;
13359                 }
13360             }
13361         }
13362         return w;
13363     },
13364     
13365     setFieldLabel : function(v)
13366     {
13367         if(!this.rendered){
13368             return;
13369         }
13370         
13371         if(this.indicatorEl()){
13372             var ar = this.el.select('label > span',true);
13373             
13374             if (ar.elements.length) {
13375                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13376                 this.fieldLabel = v;
13377                 return;
13378             }
13379             
13380             var br = this.el.select('label',true);
13381             
13382             if(br.elements.length) {
13383                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13384                 this.fieldLabel = v;
13385                 return;
13386             }
13387             
13388             Roo.log('Cannot Found any of label > span || label in input');
13389             return;
13390         }
13391         
13392         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13393         this.fieldLabel = v;
13394         
13395         
13396     }
13397 });
13398
13399  
13400 /*
13401  * - LGPL
13402  *
13403  * Input
13404  * 
13405  */
13406
13407 /**
13408  * @class Roo.bootstrap.form.TextArea
13409  * @extends Roo.bootstrap.form.Input
13410  * Bootstrap TextArea class
13411  * @cfg {Number} cols Specifies the visible width of a text area
13412  * @cfg {Number} rows Specifies the visible number of lines in a text area
13413  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13414  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13415  * @cfg {string} html text
13416  * 
13417  * @constructor
13418  * Create a new TextArea
13419  * @param {Object} config The config object
13420  */
13421
13422 Roo.bootstrap.form.TextArea = function(config){
13423     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13424    
13425 };
13426
13427 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13428      
13429     cols : false,
13430     rows : 5,
13431     readOnly : false,
13432     warp : 'soft',
13433     resize : false,
13434     value: false,
13435     html: false,
13436     
13437     getAutoCreate : function(){
13438         
13439         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13440         
13441         var id = Roo.id();
13442         
13443         var cfg = {};
13444         
13445         if(this.inputType != 'hidden'){
13446             cfg.cls = 'form-group' //input-group
13447         }
13448         
13449         var input =  {
13450             tag: 'textarea',
13451             id : id,
13452             warp : this.warp,
13453             rows : this.rows,
13454             value : this.value || '',
13455             html: this.html || '',
13456             cls : 'form-control',
13457             placeholder : this.placeholder || '' 
13458             
13459         };
13460         
13461         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13462             input.maxLength = this.maxLength;
13463         }
13464         
13465         if(this.resize){
13466             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13467         }
13468         
13469         if(this.cols){
13470             input.cols = this.cols;
13471         }
13472         
13473         if (this.readOnly) {
13474             input.readonly = true;
13475         }
13476         
13477         if (this.name) {
13478             input.name = this.name;
13479         }
13480         
13481         if (this.size) {
13482             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13483         }
13484         
13485         var settings=this;
13486         ['xs','sm','md','lg'].map(function(size){
13487             if (settings[size]) {
13488                 cfg.cls += ' col-' + size + '-' + settings[size];
13489             }
13490         });
13491         
13492         var inputblock = input;
13493         
13494         if(this.hasFeedback && !this.allowBlank){
13495             
13496             var feedback = {
13497                 tag: 'span',
13498                 cls: 'glyphicon form-control-feedback'
13499             };
13500
13501             inputblock = {
13502                 cls : 'has-feedback',
13503                 cn :  [
13504                     input,
13505                     feedback
13506                 ] 
13507             };  
13508         }
13509         
13510         
13511         if (this.before || this.after) {
13512             
13513             inputblock = {
13514                 cls : 'input-group',
13515                 cn :  [] 
13516             };
13517             if (this.before) {
13518                 inputblock.cn.push({
13519                     tag :'span',
13520                     cls : 'input-group-addon',
13521                     html : this.before
13522                 });
13523             }
13524             
13525             inputblock.cn.push(input);
13526             
13527             if(this.hasFeedback && !this.allowBlank){
13528                 inputblock.cls += ' has-feedback';
13529                 inputblock.cn.push(feedback);
13530             }
13531             
13532             if (this.after) {
13533                 inputblock.cn.push({
13534                     tag :'span',
13535                     cls : 'input-group-addon',
13536                     html : this.after
13537                 });
13538             }
13539             
13540         }
13541         
13542         
13543         cfg = this.getAutoCreateLabel( cfg, inputblock );
13544
13545          
13546         
13547         if (this.disabled) {
13548             input.disabled=true;
13549         }
13550         
13551         return cfg;
13552         
13553     },
13554     /**
13555      * return the real textarea element.
13556      */
13557     inputEl: function ()
13558     {
13559         return this.el.select('textarea.form-control',true).first();
13560     },
13561     
13562     /**
13563      * Clear any invalid styles/messages for this field
13564      */
13565     clearInvalid : function()
13566     {
13567         
13568         if(!this.el || this.preventMark){ // not rendered
13569             return;
13570         }
13571         
13572         var label = this.el.select('label', true).first();
13573         //var icon = this.el.select('i.fa-star', true).first();
13574         
13575         //if(label && icon){
13576         //    icon.remove();
13577         //}
13578         this.el.removeClass( this.validClass);
13579         this.inputEl().removeClass('is-invalid');
13580          
13581         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13582             
13583             var feedback = this.el.select('.form-control-feedback', true).first();
13584             
13585             if(feedback){
13586                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13587             }
13588             
13589         }
13590         
13591         this.fireEvent('valid', this);
13592     },
13593     
13594      /**
13595      * Mark this field as valid
13596      */
13597     markValid : function()
13598     {
13599         if(!this.el  || this.preventMark){ // not rendered
13600             return;
13601         }
13602         
13603         this.el.removeClass([this.invalidClass, this.validClass]);
13604         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13605         
13606         var feedback = this.el.select('.form-control-feedback', true).first();
13607             
13608         if(feedback){
13609             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13610         }
13611
13612         if(this.disabled || this.allowBlank){
13613             return;
13614         }
13615         
13616         var label = this.el.select('label', true).first();
13617         var icon = this.el.select('i.fa-star', true).first();
13618         
13619         //if(label && icon){
13620         //    icon.remove();
13621         //}
13622         if (Roo.bootstrap.version == 3) {
13623             this.el.addClass(this.validClass);
13624         } else {
13625             this.inputEl().addClass('is-valid');
13626         }
13627         
13628         
13629         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13630             
13631             var feedback = this.el.select('.form-control-feedback', true).first();
13632             
13633             if(feedback){
13634                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13635                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13636             }
13637             
13638         }
13639         
13640         this.fireEvent('valid', this);
13641     },
13642     
13643      /**
13644      * Mark this field as invalid
13645      * @param {String} msg The validation message
13646      */
13647     markInvalid : function(msg)
13648     {
13649         if(!this.el  || this.preventMark){ // not rendered
13650             return;
13651         }
13652         
13653         this.el.removeClass([this.invalidClass, this.validClass]);
13654         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655         
13656         var feedback = this.el.select('.form-control-feedback', true).first();
13657             
13658         if(feedback){
13659             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13660         }
13661
13662         if(this.disabled){
13663             return;
13664         }
13665         
13666         var label = this.el.select('label', true).first();
13667         //var icon = this.el.select('i.fa-star', true).first();
13668         
13669         //if(!this.getValue().length && label && !icon){
13670           /*  this.el.createChild({
13671                 tag : 'i',
13672                 cls : 'text-danger fa fa-lg fa-star',
13673                 tooltip : 'This field is required',
13674                 style : 'margin-right:5px;'
13675             }, label, true);
13676             */
13677         //}
13678         
13679         if (Roo.bootstrap.version == 3) {
13680             this.el.addClass(this.invalidClass);
13681         } else {
13682             this.inputEl().addClass('is-invalid');
13683         }
13684         
13685         // fixme ... this may be depricated need to test..
13686         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13687             
13688             var feedback = this.el.select('.form-control-feedback', true).first();
13689             
13690             if(feedback){
13691                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13692                 
13693                 if(this.getValue().length || this.forceFeedback){
13694                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13695                 }
13696                 
13697             }
13698             
13699         }
13700         
13701         this.fireEvent('invalid', this, msg);
13702     }
13703 });
13704
13705  
13706 /*
13707  * - LGPL
13708  *
13709  * trigger field - base class for combo..
13710  * 
13711  */
13712  
13713 /**
13714  * @class Roo.bootstrap.form.TriggerField
13715  * @extends Roo.bootstrap.form.Input
13716  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13717  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13718  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13719  * for which you can provide a custom implementation.  For example:
13720  * <pre><code>
13721 var trigger = new Roo.bootstrap.form.TriggerField();
13722 trigger.onTriggerClick = myTriggerFn;
13723 trigger.applyTo('my-field');
13724 </code></pre>
13725  *
13726  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13727  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13728  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13729  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13730  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13731
13732  * @constructor
13733  * Create a new TriggerField.
13734  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13735  * to the base TextField)
13736  */
13737 Roo.bootstrap.form.TriggerField = function(config){
13738     this.mimicing = false;
13739     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13740 };
13741
13742 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13743     /**
13744      * @cfg {String} triggerClass A CSS class to apply to the trigger
13745      */
13746      /**
13747      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13748      */
13749     hideTrigger:false,
13750
13751     /**
13752      * @cfg {Boolean} removable (true|false) special filter default false
13753      */
13754     removable : false,
13755     
13756     /** @cfg {Boolean} grow @hide */
13757     /** @cfg {Number} growMin @hide */
13758     /** @cfg {Number} growMax @hide */
13759
13760     /**
13761      * @hide 
13762      * @method
13763      */
13764     autoSize: Roo.emptyFn,
13765     // private
13766     monitorTab : true,
13767     // private
13768     deferHeight : true,
13769
13770     
13771     actionMode : 'wrap',
13772     
13773     caret : false,
13774     
13775     
13776     getAutoCreate : function(){
13777        
13778         var align = this.labelAlign || this.parentLabelAlign();
13779         
13780         var id = Roo.id();
13781         
13782         var cfg = {
13783             cls: 'form-group' //input-group
13784         };
13785         
13786         
13787         var input =  {
13788             tag: 'input',
13789             id : id,
13790             type : this.inputType,
13791             cls : 'form-control',
13792             autocomplete: 'new-password',
13793             placeholder : this.placeholder || '' 
13794             
13795         };
13796         if (this.name) {
13797             input.name = this.name;
13798         }
13799         if (this.size) {
13800             input.cls += ' input-' + this.size;
13801         }
13802         
13803         if (this.disabled) {
13804             input.disabled=true;
13805         }
13806         
13807         var inputblock = input;
13808         
13809         if(this.hasFeedback && !this.allowBlank){
13810             
13811             var feedback = {
13812                 tag: 'span',
13813                 cls: 'glyphicon form-control-feedback'
13814             };
13815             
13816             if(this.removable && !this.editable  ){
13817                 inputblock = {
13818                     cls : 'has-feedback',
13819                     cn :  [
13820                         inputblock,
13821                         {
13822                             tag: 'button',
13823                             html : 'x',
13824                             cls : 'roo-combo-removable-btn close'
13825                         },
13826                         feedback
13827                     ] 
13828                 };
13829             } else {
13830                 inputblock = {
13831                     cls : 'has-feedback',
13832                     cn :  [
13833                         inputblock,
13834                         feedback
13835                     ] 
13836                 };
13837             }
13838
13839         } else {
13840             if(this.removable && !this.editable ){
13841                 inputblock = {
13842                     cls : 'roo-removable',
13843                     cn :  [
13844                         inputblock,
13845                         {
13846                             tag: 'button',
13847                             html : 'x',
13848                             cls : 'roo-combo-removable-btn close'
13849                         }
13850                     ] 
13851                 };
13852             }
13853         }
13854         
13855         if (this.before || this.after) {
13856             
13857             inputblock = {
13858                 cls : 'input-group',
13859                 cn :  [] 
13860             };
13861             if (this.before) {
13862                 inputblock.cn.push({
13863                     tag :'span',
13864                     cls : 'input-group-addon input-group-prepend input-group-text',
13865                     html : this.before
13866                 });
13867             }
13868             
13869             inputblock.cn.push(input);
13870             
13871             if(this.hasFeedback && !this.allowBlank){
13872                 inputblock.cls += ' has-feedback';
13873                 inputblock.cn.push(feedback);
13874             }
13875             
13876             if (this.after) {
13877                 inputblock.cn.push({
13878                     tag :'span',
13879                     cls : 'input-group-addon input-group-append input-group-text',
13880                     html : this.after
13881                 });
13882             }
13883             
13884         };
13885         
13886       
13887         
13888         var ibwrap = inputblock;
13889         
13890         if(this.multiple){
13891             ibwrap = {
13892                 tag: 'ul',
13893                 cls: 'roo-select2-choices',
13894                 cn:[
13895                     {
13896                         tag: 'li',
13897                         cls: 'roo-select2-search-field',
13898                         cn: [
13899
13900                             inputblock
13901                         ]
13902                     }
13903                 ]
13904             };
13905                 
13906         }
13907         
13908         var combobox = {
13909             cls: 'roo-select2-container input-group',
13910             cn: [
13911                  {
13912                     tag: 'input',
13913                     type : 'hidden',
13914                     cls: 'form-hidden-field'
13915                 },
13916                 ibwrap
13917             ]
13918         };
13919         
13920         if(!this.multiple && this.showToggleBtn){
13921             
13922             var caret = {
13923                         tag: 'span',
13924                         cls: 'caret'
13925              };
13926             if (this.caret != false) {
13927                 caret = {
13928                      tag: 'i',
13929                      cls: 'fa fa-' + this.caret
13930                 };
13931                 
13932             }
13933             
13934             combobox.cn.push({
13935                 tag :'span',
13936                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13937                 cn : [
13938                     Roo.bootstrap.version == 3 ? caret : '',
13939                     {
13940                         tag: 'span',
13941                         cls: 'combobox-clear',
13942                         cn  : [
13943                             {
13944                                 tag : 'i',
13945                                 cls: 'icon-remove'
13946                             }
13947                         ]
13948                     }
13949                 ]
13950
13951             })
13952         }
13953         
13954         if(this.multiple){
13955             combobox.cls += ' roo-select2-container-multi';
13956         }
13957          var indicator = {
13958             tag : 'i',
13959             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13960             tooltip : 'This field is required'
13961         };
13962       
13963         if (this.allowBlank) {
13964             indicator = {
13965                 tag : 'i',
13966                 style : 'display:none'
13967             };
13968         }
13969          
13970         
13971         
13972         if (align ==='left' && this.fieldLabel.length) {
13973             
13974             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13975
13976             cfg.cn = [
13977                 indicator,
13978                 {
13979                     tag: 'label',
13980                     'for' :  id,
13981                     cls : 'control-label',
13982                     html : this.fieldLabel
13983
13984                 },
13985                 {
13986                     cls : "", 
13987                     cn: [
13988                         combobox
13989                     ]
13990                 }
13991
13992             ];
13993             
13994             var labelCfg = cfg.cn[1];
13995             var contentCfg = cfg.cn[2];
13996             
13997             if(this.indicatorpos == 'right'){
13998                 cfg.cn = [
13999                     {
14000                         tag: 'label',
14001                         'for' :  id,
14002                         cls : 'control-label',
14003                         cn : [
14004                             {
14005                                 tag : 'span',
14006                                 html : this.fieldLabel
14007                             },
14008                             indicator
14009                         ]
14010                     },
14011                     {
14012                         cls : "", 
14013                         cn: [
14014                             combobox
14015                         ]
14016                     }
14017
14018                 ];
14019                 
14020                 labelCfg = cfg.cn[0];
14021                 contentCfg = cfg.cn[1];
14022             }
14023             
14024             if(this.labelWidth > 12){
14025                 labelCfg.style = "width: " + this.labelWidth + 'px';
14026             }
14027             
14028             if(this.labelWidth < 13 && this.labelmd == 0){
14029                 this.labelmd = this.labelWidth;
14030             }
14031             
14032             if(this.labellg > 0){
14033                 labelCfg.cls += ' col-lg-' + this.labellg;
14034                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14035             }
14036             
14037             if(this.labelmd > 0){
14038                 labelCfg.cls += ' col-md-' + this.labelmd;
14039                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14040             }
14041             
14042             if(this.labelsm > 0){
14043                 labelCfg.cls += ' col-sm-' + this.labelsm;
14044                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14045             }
14046             
14047             if(this.labelxs > 0){
14048                 labelCfg.cls += ' col-xs-' + this.labelxs;
14049                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14050             }
14051             
14052         } else if ( this.fieldLabel.length) {
14053 //                Roo.log(" label");
14054             cfg.cn = [
14055                 indicator,
14056                {
14057                    tag: 'label',
14058                    //cls : 'input-group-addon',
14059                    html : this.fieldLabel
14060
14061                },
14062
14063                combobox
14064
14065             ];
14066             
14067             if(this.indicatorpos == 'right'){
14068                 
14069                 cfg.cn = [
14070                     {
14071                        tag: 'label',
14072                        cn : [
14073                            {
14074                                tag : 'span',
14075                                html : this.fieldLabel
14076                            },
14077                            indicator
14078                        ]
14079
14080                     },
14081                     combobox
14082
14083                 ];
14084
14085             }
14086
14087         } else {
14088             
14089 //                Roo.log(" no label && no align");
14090                 cfg = combobox
14091                      
14092                 
14093         }
14094         
14095         var settings=this;
14096         ['xs','sm','md','lg'].map(function(size){
14097             if (settings[size]) {
14098                 cfg.cls += ' col-' + size + '-' + settings[size];
14099             }
14100         });
14101         
14102         return cfg;
14103         
14104     },
14105     
14106     
14107     
14108     // private
14109     onResize : function(w, h){
14110 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14111 //        if(typeof w == 'number'){
14112 //            var x = w - this.trigger.getWidth();
14113 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14114 //            this.trigger.setStyle('left', x+'px');
14115 //        }
14116     },
14117
14118     // private
14119     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14120
14121     // private
14122     getResizeEl : function(){
14123         return this.inputEl();
14124     },
14125
14126     // private
14127     getPositionEl : function(){
14128         return this.inputEl();
14129     },
14130
14131     // private
14132     alignErrorIcon : function(){
14133         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14134     },
14135
14136     // private
14137     initEvents : function(){
14138         
14139         this.createList();
14140         
14141         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14142         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14143         if(!this.multiple && this.showToggleBtn){
14144             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14145             if(this.hideTrigger){
14146                 this.trigger.setDisplayed(false);
14147             }
14148             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14149         }
14150         
14151         if(this.multiple){
14152             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14153         }
14154         
14155         if(this.removable && !this.editable && !this.tickable){
14156             var close = this.closeTriggerEl();
14157             
14158             if(close){
14159                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14160                 close.on('click', this.removeBtnClick, this, close);
14161             }
14162         }
14163         
14164         //this.trigger.addClassOnOver('x-form-trigger-over');
14165         //this.trigger.addClassOnClick('x-form-trigger-click');
14166         
14167         //if(!this.width){
14168         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14169         //}
14170     },
14171     
14172     closeTriggerEl : function()
14173     {
14174         var close = this.el.select('.roo-combo-removable-btn', true).first();
14175         return close ? close : false;
14176     },
14177     
14178     removeBtnClick : function(e, h, el)
14179     {
14180         e.preventDefault();
14181         
14182         if(this.fireEvent("remove", this) !== false){
14183             this.reset();
14184             this.fireEvent("afterremove", this)
14185         }
14186     },
14187     
14188     createList : function()
14189     {
14190         this.list = Roo.get(document.body).createChild({
14191             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14192             cls: 'typeahead typeahead-long dropdown-menu shadow',
14193             style: 'display:none'
14194         });
14195         
14196         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14197         
14198     },
14199
14200     // private
14201     initTrigger : function(){
14202        
14203     },
14204
14205     // private
14206     onDestroy : function(){
14207         if(this.trigger){
14208             this.trigger.removeAllListeners();
14209           //  this.trigger.remove();
14210         }
14211         //if(this.wrap){
14212         //    this.wrap.remove();
14213         //}
14214         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14215     },
14216
14217     // private
14218     onFocus : function(){
14219         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14220         /*
14221         if(!this.mimicing){
14222             this.wrap.addClass('x-trigger-wrap-focus');
14223             this.mimicing = true;
14224             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14225             if(this.monitorTab){
14226                 this.el.on("keydown", this.checkTab, this);
14227             }
14228         }
14229         */
14230     },
14231
14232     // private
14233     checkTab : function(e){
14234         if(e.getKey() == e.TAB){
14235             this.triggerBlur();
14236         }
14237     },
14238
14239     // private
14240     onBlur : function(){
14241         // do nothing
14242     },
14243
14244     // private
14245     mimicBlur : function(e, t){
14246         /*
14247         if(!this.wrap.contains(t) && this.validateBlur()){
14248             this.triggerBlur();
14249         }
14250         */
14251     },
14252
14253     // private
14254     triggerBlur : function(){
14255         this.mimicing = false;
14256         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14257         if(this.monitorTab){
14258             this.el.un("keydown", this.checkTab, this);
14259         }
14260         //this.wrap.removeClass('x-trigger-wrap-focus');
14261         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14262     },
14263
14264     // private
14265     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14266     validateBlur : function(e, t){
14267         return true;
14268     },
14269
14270     // private
14271     onDisable : function(){
14272         this.inputEl().dom.disabled = true;
14273         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14274         //if(this.wrap){
14275         //    this.wrap.addClass('x-item-disabled');
14276         //}
14277     },
14278
14279     // private
14280     onEnable : function(){
14281         this.inputEl().dom.disabled = false;
14282         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14283         //if(this.wrap){
14284         //    this.el.removeClass('x-item-disabled');
14285         //}
14286     },
14287
14288     // private
14289     onShow : function(){
14290         var ae = this.getActionEl();
14291         
14292         if(ae){
14293             ae.dom.style.display = '';
14294             ae.dom.style.visibility = 'visible';
14295         }
14296     },
14297
14298     // private
14299     
14300     onHide : function(){
14301         var ae = this.getActionEl();
14302         ae.dom.style.display = 'none';
14303     },
14304
14305     /**
14306      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14307      * by an implementing function.
14308      * @method
14309      * @param {EventObject} e
14310      */
14311     onTriggerClick : Roo.emptyFn
14312 });
14313  
14314 /*
14315 * Licence: LGPL
14316 */
14317
14318 /**
14319  * @class Roo.bootstrap.form.CardUploader
14320  * @extends Roo.bootstrap.Button
14321  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14322  * @cfg {Number} errorTimeout default 3000
14323  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14324  * @cfg {Array}  html The button text.
14325
14326  *
14327  * @constructor
14328  * Create a new CardUploader
14329  * @param {Object} config The config object
14330  */
14331
14332 Roo.bootstrap.form.CardUploader = function(config){
14333     
14334  
14335     
14336     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14337     
14338     
14339     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14340         return r.data.id
14341      });
14342     
14343      this.addEvents({
14344          // raw events
14345         /**
14346          * @event preview
14347          * When a image is clicked on - and needs to display a slideshow or similar..
14348          * @param {Roo.bootstrap.Card} this
14349          * @param {Object} The image information data 
14350          *
14351          */
14352         'preview' : true,
14353          /**
14354          * @event download
14355          * When a the download link is clicked
14356          * @param {Roo.bootstrap.Card} this
14357          * @param {Object} The image information data  contains 
14358          */
14359         'download' : true
14360         
14361     });
14362 };
14363  
14364 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14365     
14366      
14367     errorTimeout : 3000,
14368      
14369     images : false,
14370    
14371     fileCollection : false,
14372     allowBlank : true,
14373     
14374     getAutoCreate : function()
14375     {
14376         
14377         var cfg =  {
14378             cls :'form-group' ,
14379             cn : [
14380                
14381                 {
14382                     tag: 'label',
14383                    //cls : 'input-group-addon',
14384                     html : this.fieldLabel
14385
14386                 },
14387
14388                 {
14389                     tag: 'input',
14390                     type : 'hidden',
14391                     name : this.name,
14392                     value : this.value,
14393                     cls : 'd-none  form-control'
14394                 },
14395                 
14396                 {
14397                     tag: 'input',
14398                     multiple : 'multiple',
14399                     type : 'file',
14400                     cls : 'd-none  roo-card-upload-selector'
14401                 },
14402                 
14403                 {
14404                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14405                 },
14406                 {
14407                     cls : 'card-columns roo-card-uploader-container'
14408                 }
14409
14410             ]
14411         };
14412            
14413          
14414         return cfg;
14415     },
14416     
14417     getChildContainer : function() /// what children are added to.
14418     {
14419         return this.containerEl;
14420     },
14421    
14422     getButtonContainer : function() /// what children are added to.
14423     {
14424         return this.el.select(".roo-card-uploader-button-container").first();
14425     },
14426    
14427     initEvents : function()
14428     {
14429         
14430         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14431         
14432         var t = this;
14433         this.addxtype({
14434             xns: Roo.bootstrap,
14435
14436             xtype : 'Button',
14437             container_method : 'getButtonContainer' ,            
14438             html :  this.html, // fix changable?
14439             cls : 'w-100 ',
14440             listeners : {
14441                 'click' : function(btn, e) {
14442                     t.onClick(e);
14443                 }
14444             }
14445         });
14446         
14447         
14448         
14449         
14450         this.urlAPI = (window.createObjectURL && window) || 
14451                                 (window.URL && URL.revokeObjectURL && URL) || 
14452                                 (window.webkitURL && webkitURL);
14453                         
14454          
14455          
14456          
14457         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14458         
14459         this.selectorEl.on('change', this.onFileSelected, this);
14460         if (this.images) {
14461             var t = this;
14462             this.images.forEach(function(img) {
14463                 t.addCard(img)
14464             });
14465             this.images = false;
14466         }
14467         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14468          
14469        
14470     },
14471     
14472    
14473     onClick : function(e)
14474     {
14475         e.preventDefault();
14476          
14477         this.selectorEl.dom.click();
14478          
14479     },
14480     
14481     onFileSelected : function(e)
14482     {
14483         e.preventDefault();
14484         
14485         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14486             return;
14487         }
14488         
14489         Roo.each(this.selectorEl.dom.files, function(file){    
14490             this.addFile(file);
14491         }, this);
14492          
14493     },
14494     
14495       
14496     
14497       
14498     
14499     addFile : function(file)
14500     {
14501            
14502         if(typeof(file) === 'string'){
14503             throw "Add file by name?"; // should not happen
14504             return;
14505         }
14506         
14507         if(!file || !this.urlAPI){
14508             return;
14509         }
14510         
14511         // file;
14512         // file.type;
14513         
14514         var _this = this;
14515         
14516         
14517         var url = _this.urlAPI.createObjectURL( file);
14518            
14519         this.addCard({
14520             id : Roo.bootstrap.form.CardUploader.ID--,
14521             is_uploaded : false,
14522             src : url,
14523             srcfile : file,
14524             title : file.name,
14525             mimetype : file.type,
14526             preview : false,
14527             is_deleted : 0
14528         });
14529         
14530     },
14531     
14532     /**
14533      * addCard - add an Attachment to the uploader
14534      * @param data - the data about the image to upload
14535      *
14536      * {
14537           id : 123
14538           title : "Title of file",
14539           is_uploaded : false,
14540           src : "http://.....",
14541           srcfile : { the File upload object },
14542           mimetype : file.type,
14543           preview : false,
14544           is_deleted : 0
14545           .. any other data...
14546         }
14547      *
14548      * 
14549     */
14550     
14551     addCard : function (data)
14552     {
14553         // hidden input element?
14554         // if the file is not an image...
14555         //then we need to use something other that and header_image
14556         var t = this;
14557         //   remove.....
14558         var footer = [
14559             {
14560                 xns : Roo.bootstrap,
14561                 xtype : 'CardFooter',
14562                  items: [
14563                     {
14564                         xns : Roo.bootstrap,
14565                         xtype : 'Element',
14566                         cls : 'd-flex',
14567                         items : [
14568                             
14569                             {
14570                                 xns : Roo.bootstrap,
14571                                 xtype : 'Button',
14572                                 html : String.format("<small>{0}</small>", data.title),
14573                                 cls : 'col-10 text-left',
14574                                 size: 'sm',
14575                                 weight: 'link',
14576                                 fa : 'download',
14577                                 listeners : {
14578                                     click : function() {
14579                                      
14580                                         t.fireEvent( "download", t, data );
14581                                     }
14582                                 }
14583                             },
14584                           
14585                             {
14586                                 xns : Roo.bootstrap,
14587                                 xtype : 'Button',
14588                                 style: 'max-height: 28px; ',
14589                                 size : 'sm',
14590                                 weight: 'danger',
14591                                 cls : 'col-2',
14592                                 fa : 'times',
14593                                 listeners : {
14594                                     click : function() {
14595                                         t.removeCard(data.id)
14596                                     }
14597                                 }
14598                             }
14599                         ]
14600                     }
14601                     
14602                 ] 
14603             }
14604             
14605         ];
14606         
14607         var cn = this.addxtype(
14608             {
14609                  
14610                 xns : Roo.bootstrap,
14611                 xtype : 'Card',
14612                 closeable : true,
14613                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14614                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14615                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14616                 data : data,
14617                 html : false,
14618                  
14619                 items : footer,
14620                 initEvents : function() {
14621                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14622                     var card = this;
14623                     this.imgEl = this.el.select('.card-img-top').first();
14624                     if (this.imgEl) {
14625                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14626                         this.imgEl.set({ 'pointer' : 'cursor' });
14627                                   
14628                     }
14629                     this.getCardFooter().addClass('p-1');
14630                     
14631                   
14632                 }
14633                 
14634             }
14635         );
14636         // dont' really need ot update items.
14637         // this.items.push(cn);
14638         this.fileCollection.add(cn);
14639         
14640         if (!data.srcfile) {
14641             this.updateInput();
14642             return;
14643         }
14644             
14645         var _t = this;
14646         var reader = new FileReader();
14647         reader.addEventListener("load", function() {  
14648             data.srcdata =  reader.result;
14649             _t.updateInput();
14650         });
14651         reader.readAsDataURL(data.srcfile);
14652         
14653         
14654         
14655     },
14656     removeCard : function(id)
14657     {
14658         
14659         var card  = this.fileCollection.get(id);
14660         card.data.is_deleted = 1;
14661         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14662         //this.fileCollection.remove(card);
14663         //this.items = this.items.filter(function(e) { return e != card });
14664         // dont' really need ot update items.
14665         card.el.dom.parentNode.removeChild(card.el.dom);
14666         this.updateInput();
14667
14668         
14669     },
14670     reset: function()
14671     {
14672         this.fileCollection.each(function(card) {
14673             if (card.el.dom && card.el.dom.parentNode) {
14674                 card.el.dom.parentNode.removeChild(card.el.dom);
14675             }
14676         });
14677         this.fileCollection.clear();
14678         this.updateInput();
14679     },
14680     
14681     updateInput : function()
14682     {
14683          var data = [];
14684         this.fileCollection.each(function(e) {
14685             data.push(e.data);
14686             
14687         });
14688         this.inputEl().dom.value = JSON.stringify(data);
14689         
14690         
14691         
14692     }
14693     
14694     
14695 });
14696
14697
14698 Roo.bootstrap.form.CardUploader.ID = -1;/*
14699  * Based on:
14700  * Ext JS Library 1.1.1
14701  * Copyright(c) 2006-2007, Ext JS, LLC.
14702  *
14703  * Originally Released Under LGPL - original licence link has changed is not relivant.
14704  *
14705  * Fork - LGPL
14706  * <script type="text/javascript">
14707  */
14708
14709
14710 /**
14711  * @class Roo.data.SortTypes
14712  * @static
14713  * Defines the default sorting (casting?) comparison functions used when sorting data.
14714  */
14715 Roo.data.SortTypes = {
14716     /**
14717      * Default sort that does nothing
14718      * @param {Mixed} s The value being converted
14719      * @return {Mixed} The comparison value
14720      */
14721     none : function(s){
14722         return s;
14723     },
14724     
14725     /**
14726      * The regular expression used to strip tags
14727      * @type {RegExp}
14728      * @property
14729      */
14730     stripTagsRE : /<\/?[^>]+>/gi,
14731     
14732     /**
14733      * Strips all HTML tags to sort on text only
14734      * @param {Mixed} s The value being converted
14735      * @return {String} The comparison value
14736      */
14737     asText : function(s){
14738         return String(s).replace(this.stripTagsRE, "");
14739     },
14740     
14741     /**
14742      * Strips all HTML tags to sort on text only - Case insensitive
14743      * @param {Mixed} s The value being converted
14744      * @return {String} The comparison value
14745      */
14746     asUCText : function(s){
14747         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14748     },
14749     
14750     /**
14751      * Case insensitive string
14752      * @param {Mixed} s The value being converted
14753      * @return {String} The comparison value
14754      */
14755     asUCString : function(s) {
14756         return String(s).toUpperCase();
14757     },
14758     
14759     /**
14760      * Date sorting
14761      * @param {Mixed} s The value being converted
14762      * @return {Number} The comparison value
14763      */
14764     asDate : function(s) {
14765         if(!s){
14766             return 0;
14767         }
14768         if(s instanceof Date){
14769             return s.getTime();
14770         }
14771         return Date.parse(String(s));
14772     },
14773     
14774     /**
14775      * Float sorting
14776      * @param {Mixed} s The value being converted
14777      * @return {Float} The comparison value
14778      */
14779     asFloat : function(s) {
14780         var val = parseFloat(String(s).replace(/,/g, ""));
14781         if(isNaN(val)) {
14782             val = 0;
14783         }
14784         return val;
14785     },
14786     
14787     /**
14788      * Integer sorting
14789      * @param {Mixed} s The value being converted
14790      * @return {Number} The comparison value
14791      */
14792     asInt : function(s) {
14793         var val = parseInt(String(s).replace(/,/g, ""));
14794         if(isNaN(val)) {
14795             val = 0;
14796         }
14797         return val;
14798     }
14799 };/*
14800  * Based on:
14801  * Ext JS Library 1.1.1
14802  * Copyright(c) 2006-2007, Ext JS, LLC.
14803  *
14804  * Originally Released Under LGPL - original licence link has changed is not relivant.
14805  *
14806  * Fork - LGPL
14807  * <script type="text/javascript">
14808  */
14809
14810 /**
14811 * @class Roo.data.Record
14812  * Instances of this class encapsulate both record <em>definition</em> information, and record
14813  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14814  * to access Records cached in an {@link Roo.data.Store} object.<br>
14815  * <p>
14816  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14817  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14818  * objects.<br>
14819  * <p>
14820  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14821  * @constructor
14822  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14823  * {@link #create}. The parameters are the same.
14824  * @param {Array} data An associative Array of data values keyed by the field name.
14825  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14826  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14827  * not specified an integer id is generated.
14828  */
14829 Roo.data.Record = function(data, id){
14830     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14831     this.data = data;
14832 };
14833
14834 /**
14835  * Generate a constructor for a specific record layout.
14836  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14837  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14838  * Each field definition object may contain the following properties: <ul>
14839  * <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,
14840  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14841  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14842  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14843  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14844  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14845  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14846  * this may be omitted.</p></li>
14847  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14848  * <ul><li>auto (Default, implies no conversion)</li>
14849  * <li>string</li>
14850  * <li>int</li>
14851  * <li>float</li>
14852  * <li>boolean</li>
14853  * <li>date</li></ul></p></li>
14854  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14855  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14856  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14857  * by the Reader into an object that will be stored in the Record. It is passed the
14858  * following parameters:<ul>
14859  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14860  * </ul></p></li>
14861  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14862  * </ul>
14863  * <br>usage:<br><pre><code>
14864 var TopicRecord = Roo.data.Record.create(
14865     {name: 'title', mapping: 'topic_title'},
14866     {name: 'author', mapping: 'username'},
14867     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14868     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14869     {name: 'lastPoster', mapping: 'user2'},
14870     {name: 'excerpt', mapping: 'post_text'}
14871 );
14872
14873 var myNewRecord = new TopicRecord({
14874     title: 'Do my job please',
14875     author: 'noobie',
14876     totalPosts: 1,
14877     lastPost: new Date(),
14878     lastPoster: 'Animal',
14879     excerpt: 'No way dude!'
14880 });
14881 myStore.add(myNewRecord);
14882 </code></pre>
14883  * @method create
14884  * @static
14885  */
14886 Roo.data.Record.create = function(o){
14887     var f = function(){
14888         f.superclass.constructor.apply(this, arguments);
14889     };
14890     Roo.extend(f, Roo.data.Record);
14891     var p = f.prototype;
14892     p.fields = new Roo.util.MixedCollection(false, function(field){
14893         return field.name;
14894     });
14895     for(var i = 0, len = o.length; i < len; i++){
14896         p.fields.add(new Roo.data.Field(o[i]));
14897     }
14898     f.getField = function(name){
14899         return p.fields.get(name);  
14900     };
14901     return f;
14902 };
14903
14904 Roo.data.Record.AUTO_ID = 1000;
14905 Roo.data.Record.EDIT = 'edit';
14906 Roo.data.Record.REJECT = 'reject';
14907 Roo.data.Record.COMMIT = 'commit';
14908
14909 Roo.data.Record.prototype = {
14910     /**
14911      * Readonly flag - true if this record has been modified.
14912      * @type Boolean
14913      */
14914     dirty : false,
14915     editing : false,
14916     error: null,
14917     modified: null,
14918
14919     // private
14920     join : function(store){
14921         this.store = store;
14922     },
14923
14924     /**
14925      * Set the named field to the specified value.
14926      * @param {String} name The name of the field to set.
14927      * @param {Object} value The value to set the field to.
14928      */
14929     set : function(name, value){
14930         if(this.data[name] == value){
14931             return;
14932         }
14933         this.dirty = true;
14934         if(!this.modified){
14935             this.modified = {};
14936         }
14937         if(typeof this.modified[name] == 'undefined'){
14938             this.modified[name] = this.data[name];
14939         }
14940         this.data[name] = value;
14941         if(!this.editing && this.store){
14942             this.store.afterEdit(this);
14943         }       
14944     },
14945
14946     /**
14947      * Get the value of the named field.
14948      * @param {String} name The name of the field to get the value of.
14949      * @return {Object} The value of the field.
14950      */
14951     get : function(name){
14952         return this.data[name]; 
14953     },
14954
14955     // private
14956     beginEdit : function(){
14957         this.editing = true;
14958         this.modified = {}; 
14959     },
14960
14961     // private
14962     cancelEdit : function(){
14963         this.editing = false;
14964         delete this.modified;
14965     },
14966
14967     // private
14968     endEdit : function(){
14969         this.editing = false;
14970         if(this.dirty && this.store){
14971             this.store.afterEdit(this);
14972         }
14973     },
14974
14975     /**
14976      * Usually called by the {@link Roo.data.Store} which owns the Record.
14977      * Rejects all changes made to the Record since either creation, or the last commit operation.
14978      * Modified fields are reverted to their original values.
14979      * <p>
14980      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14981      * of reject operations.
14982      */
14983     reject : function(){
14984         var m = this.modified;
14985         for(var n in m){
14986             if(typeof m[n] != "function"){
14987                 this.data[n] = m[n];
14988             }
14989         }
14990         this.dirty = false;
14991         delete this.modified;
14992         this.editing = false;
14993         if(this.store){
14994             this.store.afterReject(this);
14995         }
14996     },
14997
14998     /**
14999      * Usually called by the {@link Roo.data.Store} which owns the Record.
15000      * Commits all changes made to the Record since either creation, or the last commit operation.
15001      * <p>
15002      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15003      * of commit operations.
15004      */
15005     commit : function(){
15006         this.dirty = false;
15007         delete this.modified;
15008         this.editing = false;
15009         if(this.store){
15010             this.store.afterCommit(this);
15011         }
15012     },
15013
15014     // private
15015     hasError : function(){
15016         return this.error != null;
15017     },
15018
15019     // private
15020     clearError : function(){
15021         this.error = null;
15022     },
15023
15024     /**
15025      * Creates a copy of this record.
15026      * @param {String} id (optional) A new record id if you don't want to use this record's id
15027      * @return {Record}
15028      */
15029     copy : function(newId) {
15030         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15031     }
15032 };/*
15033  * Based on:
15034  * Ext JS Library 1.1.1
15035  * Copyright(c) 2006-2007, Ext JS, LLC.
15036  *
15037  * Originally Released Under LGPL - original licence link has changed is not relivant.
15038  *
15039  * Fork - LGPL
15040  * <script type="text/javascript">
15041  */
15042
15043
15044
15045 /**
15046  * @class Roo.data.Store
15047  * @extends Roo.util.Observable
15048  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15049  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15050  * <p>
15051  * 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
15052  * has no knowledge of the format of the data returned by the Proxy.<br>
15053  * <p>
15054  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15055  * instances from the data object. These records are cached and made available through accessor functions.
15056  * @constructor
15057  * Creates a new Store.
15058  * @param {Object} config A config object containing the objects needed for the Store to access data,
15059  * and read the data into Records.
15060  */
15061 Roo.data.Store = function(config){
15062     this.data = new Roo.util.MixedCollection(false);
15063     this.data.getKey = function(o){
15064         return o.id;
15065     };
15066     this.baseParams = {};
15067     // private
15068     this.paramNames = {
15069         "start" : "start",
15070         "limit" : "limit",
15071         "sort" : "sort",
15072         "dir" : "dir",
15073         "multisort" : "_multisort"
15074     };
15075
15076     if(config && config.data){
15077         this.inlineData = config.data;
15078         delete config.data;
15079     }
15080
15081     Roo.apply(this, config);
15082     
15083     if(this.reader){ // reader passed
15084         this.reader = Roo.factory(this.reader, Roo.data);
15085         this.reader.xmodule = this.xmodule || false;
15086         if(!this.recordType){
15087             this.recordType = this.reader.recordType;
15088         }
15089         if(this.reader.onMetaChange){
15090             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15091         }
15092     }
15093
15094     if(this.recordType){
15095         this.fields = this.recordType.prototype.fields;
15096     }
15097     this.modified = [];
15098
15099     this.addEvents({
15100         /**
15101          * @event datachanged
15102          * Fires when the data cache has changed, and a widget which is using this Store
15103          * as a Record cache should refresh its view.
15104          * @param {Store} this
15105          */
15106         datachanged : true,
15107         /**
15108          * @event metachange
15109          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15110          * @param {Store} this
15111          * @param {Object} meta The JSON metadata
15112          */
15113         metachange : true,
15114         /**
15115          * @event add
15116          * Fires when Records have been added to the Store
15117          * @param {Store} this
15118          * @param {Roo.data.Record[]} records The array of Records added
15119          * @param {Number} index The index at which the record(s) were added
15120          */
15121         add : true,
15122         /**
15123          * @event remove
15124          * Fires when a Record has been removed from the Store
15125          * @param {Store} this
15126          * @param {Roo.data.Record} record The Record that was removed
15127          * @param {Number} index The index at which the record was removed
15128          */
15129         remove : true,
15130         /**
15131          * @event update
15132          * Fires when a Record has been updated
15133          * @param {Store} this
15134          * @param {Roo.data.Record} record The Record that was updated
15135          * @param {String} operation The update operation being performed.  Value may be one of:
15136          * <pre><code>
15137  Roo.data.Record.EDIT
15138  Roo.data.Record.REJECT
15139  Roo.data.Record.COMMIT
15140          * </code></pre>
15141          */
15142         update : true,
15143         /**
15144          * @event clear
15145          * Fires when the data cache has been cleared.
15146          * @param {Store} this
15147          */
15148         clear : true,
15149         /**
15150          * @event beforeload
15151          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15152          * the load action will be canceled.
15153          * @param {Store} this
15154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155          */
15156         beforeload : true,
15157         /**
15158          * @event beforeloadadd
15159          * Fires after a new set of Records has been loaded.
15160          * @param {Store} this
15161          * @param {Roo.data.Record[]} records The Records that were loaded
15162          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15163          */
15164         beforeloadadd : true,
15165         /**
15166          * @event load
15167          * Fires after a new set of Records has been loaded, before they are added to the store.
15168          * @param {Store} this
15169          * @param {Roo.data.Record[]} records The Records that were loaded
15170          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15171          * @params {Object} return from reader
15172          */
15173         load : true,
15174         /**
15175          * @event loadexception
15176          * Fires if an exception occurs in the Proxy during loading.
15177          * Called with the signature of the Proxy's "loadexception" event.
15178          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15179          * 
15180          * @param {Proxy} 
15181          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15182          * @param {Object} opts - load Options
15183          * @param {Object} jsonData from your request (normally this contains the Exception)
15184          */
15185         loadexception : true
15186     });
15187     
15188     if(this.proxy){
15189         this.proxy = Roo.factory(this.proxy, Roo.data);
15190         this.proxy.xmodule = this.xmodule || false;
15191         this.relayEvents(this.proxy,  ["loadexception"]);
15192     }
15193     this.sortToggle = {};
15194     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15195
15196     Roo.data.Store.superclass.constructor.call(this);
15197
15198     if(this.inlineData){
15199         this.loadData(this.inlineData);
15200         delete this.inlineData;
15201     }
15202 };
15203
15204 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15205      /**
15206     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15207     * without a remote query - used by combo/forms at present.
15208     */
15209     
15210     /**
15211     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15212     */
15213     /**
15214     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15215     */
15216     /**
15217     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15218     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15219     */
15220     /**
15221     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15222     * on any HTTP request
15223     */
15224     /**
15225     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15226     */
15227     /**
15228     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15229     */
15230     multiSort: false,
15231     /**
15232     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15233     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15234     */
15235     remoteSort : false,
15236
15237     /**
15238     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15239      * loaded or when a record is removed. (defaults to false).
15240     */
15241     pruneModifiedRecords : false,
15242
15243     // private
15244     lastOptions : null,
15245
15246     /**
15247      * Add Records to the Store and fires the add event.
15248      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15249      */
15250     add : function(records){
15251         records = [].concat(records);
15252         for(var i = 0, len = records.length; i < len; i++){
15253             records[i].join(this);
15254         }
15255         var index = this.data.length;
15256         this.data.addAll(records);
15257         this.fireEvent("add", this, records, index);
15258     },
15259
15260     /**
15261      * Remove a Record from the Store and fires the remove event.
15262      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15263      */
15264     remove : function(record){
15265         var index = this.data.indexOf(record);
15266         this.data.removeAt(index);
15267  
15268         if(this.pruneModifiedRecords){
15269             this.modified.remove(record);
15270         }
15271         this.fireEvent("remove", this, record, index);
15272     },
15273
15274     /**
15275      * Remove all Records from the Store and fires the clear event.
15276      */
15277     removeAll : function(){
15278         this.data.clear();
15279         if(this.pruneModifiedRecords){
15280             this.modified = [];
15281         }
15282         this.fireEvent("clear", this);
15283     },
15284
15285     /**
15286      * Inserts Records to the Store at the given index and fires the add event.
15287      * @param {Number} index The start index at which to insert the passed Records.
15288      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15289      */
15290     insert : function(index, records){
15291         records = [].concat(records);
15292         for(var i = 0, len = records.length; i < len; i++){
15293             this.data.insert(index, records[i]);
15294             records[i].join(this);
15295         }
15296         this.fireEvent("add", this, records, index);
15297     },
15298
15299     /**
15300      * Get the index within the cache of the passed Record.
15301      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15302      * @return {Number} The index of the passed Record. Returns -1 if not found.
15303      */
15304     indexOf : function(record){
15305         return this.data.indexOf(record);
15306     },
15307
15308     /**
15309      * Get the index within the cache of the Record with the passed id.
15310      * @param {String} id The id of the Record to find.
15311      * @return {Number} The index of the Record. Returns -1 if not found.
15312      */
15313     indexOfId : function(id){
15314         return this.data.indexOfKey(id);
15315     },
15316
15317     /**
15318      * Get the Record with the specified id.
15319      * @param {String} id The id of the Record to find.
15320      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15321      */
15322     getById : function(id){
15323         return this.data.key(id);
15324     },
15325
15326     /**
15327      * Get the Record at the specified index.
15328      * @param {Number} index The index of the Record to find.
15329      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15330      */
15331     getAt : function(index){
15332         return this.data.itemAt(index);
15333     },
15334
15335     /**
15336      * Returns a range of Records between specified indices.
15337      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15338      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15339      * @return {Roo.data.Record[]} An array of Records
15340      */
15341     getRange : function(start, end){
15342         return this.data.getRange(start, end);
15343     },
15344
15345     // private
15346     storeOptions : function(o){
15347         o = Roo.apply({}, o);
15348         delete o.callback;
15349         delete o.scope;
15350         this.lastOptions = o;
15351     },
15352
15353     /**
15354      * Loads the Record cache from the configured Proxy using the configured Reader.
15355      * <p>
15356      * If using remote paging, then the first load call must specify the <em>start</em>
15357      * and <em>limit</em> properties in the options.params property to establish the initial
15358      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15359      * <p>
15360      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15361      * and this call will return before the new data has been loaded. Perform any post-processing
15362      * in a callback function, or in a "load" event handler.</strong>
15363      * <p>
15364      * @param {Object} options An object containing properties which control loading options:<ul>
15365      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15366      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15367      * <pre>
15368                 {
15369                     data : data,  // array of key=>value data like JsonReader
15370                     total : data.length,
15371                     success : true
15372                     
15373                 }
15374         </pre>
15375             }.</li>
15376      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15377      * passed the following arguments:<ul>
15378      * <li>r : Roo.data.Record[]</li>
15379      * <li>options: Options object from the load call</li>
15380      * <li>success: Boolean success indicator</li></ul></li>
15381      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15382      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15383      * </ul>
15384      */
15385     load : function(options){
15386         options = options || {};
15387         if(this.fireEvent("beforeload", this, options) !== false){
15388             this.storeOptions(options);
15389             var p = Roo.apply(options.params || {}, this.baseParams);
15390             // if meta was not loaded from remote source.. try requesting it.
15391             if (!this.reader.metaFromRemote) {
15392                 p._requestMeta = 1;
15393             }
15394             if(this.sortInfo && this.remoteSort){
15395                 var pn = this.paramNames;
15396                 p[pn["sort"]] = this.sortInfo.field;
15397                 p[pn["dir"]] = this.sortInfo.direction;
15398             }
15399             if (this.multiSort) {
15400                 var pn = this.paramNames;
15401                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15402             }
15403             
15404             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15405         }
15406     },
15407
15408     /**
15409      * Reloads the Record cache from the configured Proxy using the configured Reader and
15410      * the options from the last load operation performed.
15411      * @param {Object} options (optional) An object containing properties which may override the options
15412      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15413      * the most recently used options are reused).
15414      */
15415     reload : function(options){
15416         this.load(Roo.applyIf(options||{}, this.lastOptions));
15417     },
15418
15419     // private
15420     // Called as a callback by the Reader during a load operation.
15421     loadRecords : function(o, options, success){
15422          
15423         if(!o){
15424             if(success !== false){
15425                 this.fireEvent("load", this, [], options, o);
15426             }
15427             if(options.callback){
15428                 options.callback.call(options.scope || this, [], options, false);
15429             }
15430             return;
15431         }
15432         // if data returned failure - throw an exception.
15433         if (o.success === false) {
15434             // show a message if no listener is registered.
15435             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15436                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15437             }
15438             // loadmask wil be hooked into this..
15439             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15440             return;
15441         }
15442         var r = o.records, t = o.totalRecords || r.length;
15443         
15444         this.fireEvent("beforeloadadd", this, r, options, o);
15445         
15446         if(!options || options.add !== true){
15447             if(this.pruneModifiedRecords){
15448                 this.modified = [];
15449             }
15450             for(var i = 0, len = r.length; i < len; i++){
15451                 r[i].join(this);
15452             }
15453             if(this.snapshot){
15454                 this.data = this.snapshot;
15455                 delete this.snapshot;
15456             }
15457             this.data.clear();
15458             this.data.addAll(r);
15459             this.totalLength = t;
15460             this.applySort();
15461             this.fireEvent("datachanged", this);
15462         }else{
15463             this.totalLength = Math.max(t, this.data.length+r.length);
15464             this.add(r);
15465         }
15466         
15467         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15468                 
15469             var e = new Roo.data.Record({});
15470
15471             e.set(this.parent.displayField, this.parent.emptyTitle);
15472             e.set(this.parent.valueField, '');
15473
15474             this.insert(0, e);
15475         }
15476             
15477         this.fireEvent("load", this, r, options, o);
15478         if(options.callback){
15479             options.callback.call(options.scope || this, r, options, true);
15480         }
15481     },
15482
15483
15484     /**
15485      * Loads data from a passed data block. A Reader which understands the format of the data
15486      * must have been configured in the constructor.
15487      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15488      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15489      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15490      */
15491     loadData : function(o, append){
15492         var r = this.reader.readRecords(o);
15493         this.loadRecords(r, {add: append}, true);
15494     },
15495     
15496      /**
15497      * using 'cn' the nested child reader read the child array into it's child stores.
15498      * @param {Object} rec The record with a 'children array
15499      */
15500     loadDataFromChildren : function(rec)
15501     {
15502         this.loadData(this.reader.toLoadData(rec));
15503     },
15504     
15505
15506     /**
15507      * Gets the number of cached records.
15508      * <p>
15509      * <em>If using paging, this may not be the total size of the dataset. If the data object
15510      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15511      * the data set size</em>
15512      */
15513     getCount : function(){
15514         return this.data.length || 0;
15515     },
15516
15517     /**
15518      * Gets the total number of records in the dataset as returned by the server.
15519      * <p>
15520      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15521      * the dataset size</em>
15522      */
15523     getTotalCount : function(){
15524         return this.totalLength || 0;
15525     },
15526
15527     /**
15528      * Returns the sort state of the Store as an object with two properties:
15529      * <pre><code>
15530  field {String} The name of the field by which the Records are sorted
15531  direction {String} The sort order, "ASC" or "DESC"
15532      * </code></pre>
15533      */
15534     getSortState : function(){
15535         return this.sortInfo;
15536     },
15537
15538     // private
15539     applySort : function(){
15540         if(this.sortInfo && !this.remoteSort){
15541             var s = this.sortInfo, f = s.field;
15542             var st = this.fields.get(f).sortType;
15543             var fn = function(r1, r2){
15544                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15545                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15546             };
15547             this.data.sort(s.direction, fn);
15548             if(this.snapshot && this.snapshot != this.data){
15549                 this.snapshot.sort(s.direction, fn);
15550             }
15551         }
15552     },
15553
15554     /**
15555      * Sets the default sort column and order to be used by the next load operation.
15556      * @param {String} fieldName The name of the field to sort by.
15557      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15558      */
15559     setDefaultSort : function(field, dir){
15560         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15561     },
15562
15563     /**
15564      * Sort the Records.
15565      * If remote sorting is used, the sort is performed on the server, and the cache is
15566      * reloaded. If local sorting is used, the cache is sorted internally.
15567      * @param {String} fieldName The name of the field to sort by.
15568      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15569      */
15570     sort : function(fieldName, dir){
15571         var f = this.fields.get(fieldName);
15572         if(!dir){
15573             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15574             
15575             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15576                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15577             }else{
15578                 dir = f.sortDir;
15579             }
15580         }
15581         this.sortToggle[f.name] = dir;
15582         this.sortInfo = {field: f.name, direction: dir};
15583         if(!this.remoteSort){
15584             this.applySort();
15585             this.fireEvent("datachanged", this);
15586         }else{
15587             this.load(this.lastOptions);
15588         }
15589     },
15590
15591     /**
15592      * Calls the specified function for each of the Records in the cache.
15593      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15594      * Returning <em>false</em> aborts and exits the iteration.
15595      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15596      */
15597     each : function(fn, scope){
15598         this.data.each(fn, scope);
15599     },
15600
15601     /**
15602      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15603      * (e.g., during paging).
15604      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15605      */
15606     getModifiedRecords : function(){
15607         return this.modified;
15608     },
15609
15610     // private
15611     createFilterFn : function(property, value, anyMatch){
15612         if(!value.exec){ // not a regex
15613             value = String(value);
15614             if(value.length == 0){
15615                 return false;
15616             }
15617             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15618         }
15619         return function(r){
15620             return value.test(r.data[property]);
15621         };
15622     },
15623
15624     /**
15625      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15626      * @param {String} property A field on your records
15627      * @param {Number} start The record index to start at (defaults to 0)
15628      * @param {Number} end The last record index to include (defaults to length - 1)
15629      * @return {Number} The sum
15630      */
15631     sum : function(property, start, end){
15632         var rs = this.data.items, v = 0;
15633         start = start || 0;
15634         end = (end || end === 0) ? end : rs.length-1;
15635
15636         for(var i = start; i <= end; i++){
15637             v += (rs[i].data[property] || 0);
15638         }
15639         return v;
15640     },
15641
15642     /**
15643      * Filter the records by a specified property.
15644      * @param {String} field A field on your records
15645      * @param {String/RegExp} value Either a string that the field
15646      * should start with or a RegExp to test against the field
15647      * @param {Boolean} anyMatch True to match any part not just the beginning
15648      */
15649     filter : function(property, value, anyMatch){
15650         var fn = this.createFilterFn(property, value, anyMatch);
15651         return fn ? this.filterBy(fn) : this.clearFilter();
15652     },
15653
15654     /**
15655      * Filter by a function. The specified function will be called with each
15656      * record in this data source. If the function returns true the record is included,
15657      * otherwise it is filtered.
15658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15659      * @param {Object} scope (optional) The scope of the function (defaults to this)
15660      */
15661     filterBy : function(fn, scope){
15662         this.snapshot = this.snapshot || this.data;
15663         this.data = this.queryBy(fn, scope||this);
15664         this.fireEvent("datachanged", this);
15665     },
15666
15667     /**
15668      * Query the records by a specified property.
15669      * @param {String} field A field on your records
15670      * @param {String/RegExp} value Either a string that the field
15671      * should start with or a RegExp to test against the field
15672      * @param {Boolean} anyMatch True to match any part not just the beginning
15673      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15674      */
15675     query : function(property, value, anyMatch){
15676         var fn = this.createFilterFn(property, value, anyMatch);
15677         return fn ? this.queryBy(fn) : this.data.clone();
15678     },
15679
15680     /**
15681      * Query by a function. The specified function will be called with each
15682      * record in this data source. If the function returns true the record is included
15683      * in the results.
15684      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15685      * @param {Object} scope (optional) The scope of the function (defaults to this)
15686       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15687      **/
15688     queryBy : function(fn, scope){
15689         var data = this.snapshot || this.data;
15690         return data.filterBy(fn, scope||this);
15691     },
15692
15693     /**
15694      * Collects unique values for a particular dataIndex from this store.
15695      * @param {String} dataIndex The property to collect
15696      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15697      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15698      * @return {Array} An array of the unique values
15699      **/
15700     collect : function(dataIndex, allowNull, bypassFilter){
15701         var d = (bypassFilter === true && this.snapshot) ?
15702                 this.snapshot.items : this.data.items;
15703         var v, sv, r = [], l = {};
15704         for(var i = 0, len = d.length; i < len; i++){
15705             v = d[i].data[dataIndex];
15706             sv = String(v);
15707             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15708                 l[sv] = true;
15709                 r[r.length] = v;
15710             }
15711         }
15712         return r;
15713     },
15714
15715     /**
15716      * Revert to a view of the Record cache with no filtering applied.
15717      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15718      */
15719     clearFilter : function(suppressEvent){
15720         if(this.snapshot && this.snapshot != this.data){
15721             this.data = this.snapshot;
15722             delete this.snapshot;
15723             if(suppressEvent !== true){
15724                 this.fireEvent("datachanged", this);
15725             }
15726         }
15727     },
15728
15729     // private
15730     afterEdit : function(record){
15731         if(this.modified.indexOf(record) == -1){
15732             this.modified.push(record);
15733         }
15734         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15735     },
15736     
15737     // private
15738     afterReject : function(record){
15739         this.modified.remove(record);
15740         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15741     },
15742
15743     // private
15744     afterCommit : function(record){
15745         this.modified.remove(record);
15746         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15747     },
15748
15749     /**
15750      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15751      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15752      */
15753     commitChanges : function(){
15754         var m = this.modified.slice(0);
15755         this.modified = [];
15756         for(var i = 0, len = m.length; i < len; i++){
15757             m[i].commit();
15758         }
15759     },
15760
15761     /**
15762      * Cancel outstanding changes on all changed records.
15763      */
15764     rejectChanges : function(){
15765         var m = this.modified.slice(0);
15766         this.modified = [];
15767         for(var i = 0, len = m.length; i < len; i++){
15768             m[i].reject();
15769         }
15770     },
15771
15772     onMetaChange : function(meta, rtype, o){
15773         this.recordType = rtype;
15774         this.fields = rtype.prototype.fields;
15775         delete this.snapshot;
15776         this.sortInfo = meta.sortInfo || this.sortInfo;
15777         this.modified = [];
15778         this.fireEvent('metachange', this, this.reader.meta);
15779     },
15780     
15781     moveIndex : function(data, type)
15782     {
15783         var index = this.indexOf(data);
15784         
15785         var newIndex = index + type;
15786         
15787         this.remove(data);
15788         
15789         this.insert(newIndex, data);
15790         
15791     }
15792 });/*
15793  * Based on:
15794  * Ext JS Library 1.1.1
15795  * Copyright(c) 2006-2007, Ext JS, LLC.
15796  *
15797  * Originally Released Under LGPL - original licence link has changed is not relivant.
15798  *
15799  * Fork - LGPL
15800  * <script type="text/javascript">
15801  */
15802
15803 /**
15804  * @class Roo.data.SimpleStore
15805  * @extends Roo.data.Store
15806  * Small helper class to make creating Stores from Array data easier.
15807  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15808  * @cfg {Array} fields An array of field definition objects, or field name strings.
15809  * @cfg {Object} an existing reader (eg. copied from another store)
15810  * @cfg {Array} data The multi-dimensional array of data
15811  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15812  * @cfg {Roo.data.Reader} reader  [not-required] 
15813  * @constructor
15814  * @param {Object} config
15815  */
15816 Roo.data.SimpleStore = function(config)
15817 {
15818     Roo.data.SimpleStore.superclass.constructor.call(this, {
15819         isLocal : true,
15820         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15821                 id: config.id
15822             },
15823             Roo.data.Record.create(config.fields)
15824         ),
15825         proxy : new Roo.data.MemoryProxy(config.data)
15826     });
15827     this.load();
15828 };
15829 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15830  * Based on:
15831  * Ext JS Library 1.1.1
15832  * Copyright(c) 2006-2007, Ext JS, LLC.
15833  *
15834  * Originally Released Under LGPL - original licence link has changed is not relivant.
15835  *
15836  * Fork - LGPL
15837  * <script type="text/javascript">
15838  */
15839
15840 /**
15841 /**
15842  * @extends Roo.data.Store
15843  * @class Roo.data.JsonStore
15844  * Small helper class to make creating Stores for JSON data easier. <br/>
15845 <pre><code>
15846 var store = new Roo.data.JsonStore({
15847     url: 'get-images.php',
15848     root: 'images',
15849     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15850 });
15851 </code></pre>
15852  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15853  * JsonReader and HttpProxy (unless inline data is provided).</b>
15854  * @cfg {Array} fields An array of field definition objects, or field name strings.
15855  * @constructor
15856  * @param {Object} config
15857  */
15858 Roo.data.JsonStore = function(c){
15859     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15860         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15861         reader: new Roo.data.JsonReader(c, c.fields)
15862     }));
15863 };
15864 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15865  * Based on:
15866  * Ext JS Library 1.1.1
15867  * Copyright(c) 2006-2007, Ext JS, LLC.
15868  *
15869  * Originally Released Under LGPL - original licence link has changed is not relivant.
15870  *
15871  * Fork - LGPL
15872  * <script type="text/javascript">
15873  */
15874
15875  
15876 Roo.data.Field = function(config){
15877     if(typeof config == "string"){
15878         config = {name: config};
15879     }
15880     Roo.apply(this, config);
15881     
15882     if(!this.type){
15883         this.type = "auto";
15884     }
15885     
15886     var st = Roo.data.SortTypes;
15887     // named sortTypes are supported, here we look them up
15888     if(typeof this.sortType == "string"){
15889         this.sortType = st[this.sortType];
15890     }
15891     
15892     // set default sortType for strings and dates
15893     if(!this.sortType){
15894         switch(this.type){
15895             case "string":
15896                 this.sortType = st.asUCString;
15897                 break;
15898             case "date":
15899                 this.sortType = st.asDate;
15900                 break;
15901             default:
15902                 this.sortType = st.none;
15903         }
15904     }
15905
15906     // define once
15907     var stripRe = /[\$,%]/g;
15908
15909     // prebuilt conversion function for this field, instead of
15910     // switching every time we're reading a value
15911     if(!this.convert){
15912         var cv, dateFormat = this.dateFormat;
15913         switch(this.type){
15914             case "":
15915             case "auto":
15916             case undefined:
15917                 cv = function(v){ return v; };
15918                 break;
15919             case "string":
15920                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15921                 break;
15922             case "int":
15923                 cv = function(v){
15924                     return v !== undefined && v !== null && v !== '' ?
15925                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15926                     };
15927                 break;
15928             case "float":
15929                 cv = function(v){
15930                     return v !== undefined && v !== null && v !== '' ?
15931                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15932                     };
15933                 break;
15934             case "bool":
15935             case "boolean":
15936                 cv = function(v){ return v === true || v === "true" || v == 1; };
15937                 break;
15938             case "date":
15939                 cv = function(v){
15940                     if(!v){
15941                         return '';
15942                     }
15943                     if(v instanceof Date){
15944                         return v;
15945                     }
15946                     if(dateFormat){
15947                         if(dateFormat == "timestamp"){
15948                             return new Date(v*1000);
15949                         }
15950                         return Date.parseDate(v, dateFormat);
15951                     }
15952                     var parsed = Date.parse(v);
15953                     return parsed ? new Date(parsed) : null;
15954                 };
15955              break;
15956             
15957         }
15958         this.convert = cv;
15959     }
15960 };
15961
15962 Roo.data.Field.prototype = {
15963     dateFormat: null,
15964     defaultValue: "",
15965     mapping: null,
15966     sortType : null,
15967     sortDir : "ASC"
15968 };/*
15969  * Based on:
15970  * Ext JS Library 1.1.1
15971  * Copyright(c) 2006-2007, Ext JS, LLC.
15972  *
15973  * Originally Released Under LGPL - original licence link has changed is not relivant.
15974  *
15975  * Fork - LGPL
15976  * <script type="text/javascript">
15977  */
15978  
15979 // Base class for reading structured data from a data source.  This class is intended to be
15980 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15981
15982 /**
15983  * @class Roo.data.DataReader
15984  * @abstract
15985  * Base class for reading structured data from a data source.  This class is intended to be
15986  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15987  */
15988
15989 Roo.data.DataReader = function(meta, recordType){
15990     
15991     this.meta = meta;
15992     
15993     this.recordType = recordType instanceof Array ? 
15994         Roo.data.Record.create(recordType) : recordType;
15995 };
15996
15997 Roo.data.DataReader.prototype = {
15998     
15999     
16000     readerType : 'Data',
16001      /**
16002      * Create an empty record
16003      * @param {Object} data (optional) - overlay some values
16004      * @return {Roo.data.Record} record created.
16005      */
16006     newRow :  function(d) {
16007         var da =  {};
16008         this.recordType.prototype.fields.each(function(c) {
16009             switch( c.type) {
16010                 case 'int' : da[c.name] = 0; break;
16011                 case 'date' : da[c.name] = new Date(); break;
16012                 case 'float' : da[c.name] = 0.0; break;
16013                 case 'boolean' : da[c.name] = false; break;
16014                 default : da[c.name] = ""; break;
16015             }
16016             
16017         });
16018         return new this.recordType(Roo.apply(da, d));
16019     }
16020     
16021     
16022 };/*
16023  * Based on:
16024  * Ext JS Library 1.1.1
16025  * Copyright(c) 2006-2007, Ext JS, LLC.
16026  *
16027  * Originally Released Under LGPL - original licence link has changed is not relivant.
16028  *
16029  * Fork - LGPL
16030  * <script type="text/javascript">
16031  */
16032
16033 /**
16034  * @class Roo.data.DataProxy
16035  * @extends Roo.util.Observable
16036  * @abstract
16037  * This class is an abstract base class for implementations which provide retrieval of
16038  * unformatted data objects.<br>
16039  * <p>
16040  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16041  * (of the appropriate type which knows how to parse the data object) to provide a block of
16042  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16043  * <p>
16044  * Custom implementations must implement the load method as described in
16045  * {@link Roo.data.HttpProxy#load}.
16046  */
16047 Roo.data.DataProxy = function(){
16048     this.addEvents({
16049         /**
16050          * @event beforeload
16051          * Fires before a network request is made to retrieve a data object.
16052          * @param {Object} This DataProxy object.
16053          * @param {Object} params The params parameter to the load function.
16054          */
16055         beforeload : true,
16056         /**
16057          * @event load
16058          * Fires before the load method's callback is called.
16059          * @param {Object} This DataProxy object.
16060          * @param {Object} o The data object.
16061          * @param {Object} arg The callback argument object passed to the load function.
16062          */
16063         load : true,
16064         /**
16065          * @event loadexception
16066          * Fires if an Exception occurs during data retrieval.
16067          * @param {Object} This DataProxy object.
16068          * @param {Object} o The data object.
16069          * @param {Object} arg The callback argument object passed to the load function.
16070          * @param {Object} e The Exception.
16071          */
16072         loadexception : true
16073     });
16074     Roo.data.DataProxy.superclass.constructor.call(this);
16075 };
16076
16077 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16078
16079     /**
16080      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16081      */
16082 /*
16083  * Based on:
16084  * Ext JS Library 1.1.1
16085  * Copyright(c) 2006-2007, Ext JS, LLC.
16086  *
16087  * Originally Released Under LGPL - original licence link has changed is not relivant.
16088  *
16089  * Fork - LGPL
16090  * <script type="text/javascript">
16091  */
16092 /**
16093  * @class Roo.data.MemoryProxy
16094  * @extends Roo.data.DataProxy
16095  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16096  * to the Reader when its load method is called.
16097  * @constructor
16098  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16099  */
16100 Roo.data.MemoryProxy = function(config){
16101     var data = config;
16102     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16103         data = config.data;
16104     }
16105     Roo.data.MemoryProxy.superclass.constructor.call(this);
16106     this.data = data;
16107 };
16108
16109 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16110     
16111     /**
16112      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16113      */
16114     /**
16115      * Load data from the requested source (in this case an in-memory
16116      * data object passed to the constructor), read the data object into
16117      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16118      * process that block using the passed callback.
16119      * @param {Object} params This parameter is not used by the MemoryProxy class.
16120      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16121      * object into a block of Roo.data.Records.
16122      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16123      * The function must be passed <ul>
16124      * <li>The Record block object</li>
16125      * <li>The "arg" argument from the load function</li>
16126      * <li>A boolean success indicator</li>
16127      * </ul>
16128      * @param {Object} scope The scope in which to call the callback
16129      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16130      */
16131     load : function(params, reader, callback, scope, arg){
16132         params = params || {};
16133         var result;
16134         try {
16135             result = reader.readRecords(params.data ? params.data :this.data);
16136         }catch(e){
16137             this.fireEvent("loadexception", this, arg, null, e);
16138             callback.call(scope, null, arg, false);
16139             return;
16140         }
16141         callback.call(scope, result, arg, true);
16142     },
16143     
16144     // private
16145     update : function(params, records){
16146         
16147     }
16148 });/*
16149  * Based on:
16150  * Ext JS Library 1.1.1
16151  * Copyright(c) 2006-2007, Ext JS, LLC.
16152  *
16153  * Originally Released Under LGPL - original licence link has changed is not relivant.
16154  *
16155  * Fork - LGPL
16156  * <script type="text/javascript">
16157  */
16158 /**
16159  * @class Roo.data.HttpProxy
16160  * @extends Roo.data.DataProxy
16161  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16162  * configured to reference a certain URL.<br><br>
16163  * <p>
16164  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16165  * from which the running page was served.<br><br>
16166  * <p>
16167  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16168  * <p>
16169  * Be aware that to enable the browser to parse an XML document, the server must set
16170  * the Content-Type header in the HTTP response to "text/xml".
16171  * @constructor
16172  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16173  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16174  * will be used to make the request.
16175  */
16176 Roo.data.HttpProxy = function(conn){
16177     Roo.data.HttpProxy.superclass.constructor.call(this);
16178     // is conn a conn config or a real conn?
16179     this.conn = conn;
16180     this.useAjax = !conn || !conn.events;
16181   
16182 };
16183
16184 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16185     // thse are take from connection...
16186     
16187     /**
16188      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16189      */
16190     /**
16191      * @cfg {Object} extraParams  An object containing properties which are used as
16192      * extra parameters to each request made by this object. (defaults to undefined)
16193      */
16194     /**
16195      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16196      *  to each request made by this object. (defaults to undefined)
16197      */
16198     /**
16199      * @cfg {String} method (GET|POST)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16200      */
16201     /**
16202      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16203      */
16204      /**
16205      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16206      * @type Boolean
16207      */
16208   
16209
16210     /**
16211      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16212      * @type Boolean
16213      */
16214     /**
16215      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16216      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16217      * a finer-grained basis than the DataProxy events.
16218      */
16219     getConnection : function(){
16220         return this.useAjax ? Roo.Ajax : this.conn;
16221     },
16222
16223     /**
16224      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16225      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16226      * process that block using the passed callback.
16227      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16228      * for the request to the remote server.
16229      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16230      * object into a block of Roo.data.Records.
16231      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16232      * The function must be passed <ul>
16233      * <li>The Record block object</li>
16234      * <li>The "arg" argument from the load function</li>
16235      * <li>A boolean success indicator</li>
16236      * </ul>
16237      * @param {Object} scope The scope in which to call the callback
16238      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16239      */
16240     load : function(params, reader, callback, scope, arg){
16241         if(this.fireEvent("beforeload", this, params) !== false){
16242             var  o = {
16243                 params : params || {},
16244                 request: {
16245                     callback : callback,
16246                     scope : scope,
16247                     arg : arg
16248                 },
16249                 reader: reader,
16250                 callback : this.loadResponse,
16251                 scope: this
16252             };
16253             if(this.useAjax){
16254                 Roo.applyIf(o, this.conn);
16255                 if(this.activeRequest){
16256                     Roo.Ajax.abort(this.activeRequest);
16257                 }
16258                 this.activeRequest = Roo.Ajax.request(o);
16259             }else{
16260                 this.conn.request(o);
16261             }
16262         }else{
16263             callback.call(scope||this, null, arg, false);
16264         }
16265     },
16266
16267     // private
16268     loadResponse : function(o, success, response){
16269         delete this.activeRequest;
16270         if(!success){
16271             this.fireEvent("loadexception", this, o, response);
16272             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16273             return;
16274         }
16275         var result;
16276         try {
16277             result = o.reader.read(response);
16278         }catch(e){
16279             o.success = false;
16280             o.raw = { errorMsg : response.responseText };
16281             this.fireEvent("loadexception", this, o, response, e);
16282             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16283             return;
16284         }
16285         
16286         this.fireEvent("load", this, o, o.request.arg);
16287         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16288     },
16289
16290     // private
16291     update : function(dataSet){
16292
16293     },
16294
16295     // private
16296     updateResponse : function(dataSet){
16297
16298     }
16299 });/*
16300  * Based on:
16301  * Ext JS Library 1.1.1
16302  * Copyright(c) 2006-2007, Ext JS, LLC.
16303  *
16304  * Originally Released Under LGPL - original licence link has changed is not relivant.
16305  *
16306  * Fork - LGPL
16307  * <script type="text/javascript">
16308  */
16309
16310 /**
16311  * @class Roo.data.ScriptTagProxy
16312  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16313  * other than the originating domain of the running page.<br><br>
16314  * <p>
16315  * <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
16316  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16317  * <p>
16318  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16319  * source code that is used as the source inside a &lt;script> tag.<br><br>
16320  * <p>
16321  * In order for the browser to process the returned data, the server must wrap the data object
16322  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16323  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16324  * depending on whether the callback name was passed:
16325  * <p>
16326  * <pre><code>
16327 boolean scriptTag = false;
16328 String cb = request.getParameter("callback");
16329 if (cb != null) {
16330     scriptTag = true;
16331     response.setContentType("text/javascript");
16332 } else {
16333     response.setContentType("application/x-json");
16334 }
16335 Writer out = response.getWriter();
16336 if (scriptTag) {
16337     out.write(cb + "(");
16338 }
16339 out.print(dataBlock.toJsonString());
16340 if (scriptTag) {
16341     out.write(");");
16342 }
16343 </pre></code>
16344  *
16345  * @constructor
16346  * @param {Object} config A configuration object.
16347  */
16348 Roo.data.ScriptTagProxy = function(config){
16349     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16350     Roo.apply(this, config);
16351     this.head = document.getElementsByTagName("head")[0];
16352 };
16353
16354 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16355
16356 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16357     /**
16358      * @cfg {String} url The URL from which to request the data object.
16359      */
16360     /**
16361      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16362      */
16363     timeout : 30000,
16364     /**
16365      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16366      * the server the name of the callback function set up by the load call to process the returned data object.
16367      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16368      * javascript output which calls this named function passing the data object as its only parameter.
16369      */
16370     callbackParam : "callback",
16371     /**
16372      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16373      * name to the request.
16374      */
16375     nocache : true,
16376
16377     /**
16378      * Load data from the configured URL, read the data object into
16379      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16380      * process that block using the passed callback.
16381      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16382      * for the request to the remote server.
16383      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16384      * object into a block of Roo.data.Records.
16385      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16386      * The function must be passed <ul>
16387      * <li>The Record block object</li>
16388      * <li>The "arg" argument from the load function</li>
16389      * <li>A boolean success indicator</li>
16390      * </ul>
16391      * @param {Object} scope The scope in which to call the callback
16392      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16393      */
16394     load : function(params, reader, callback, scope, arg){
16395         if(this.fireEvent("beforeload", this, params) !== false){
16396
16397             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16398
16399             var url = this.url;
16400             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16401             if(this.nocache){
16402                 url += "&_dc=" + (new Date().getTime());
16403             }
16404             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16405             var trans = {
16406                 id : transId,
16407                 cb : "stcCallback"+transId,
16408                 scriptId : "stcScript"+transId,
16409                 params : params,
16410                 arg : arg,
16411                 url : url,
16412                 callback : callback,
16413                 scope : scope,
16414                 reader : reader
16415             };
16416             var conn = this;
16417
16418             window[trans.cb] = function(o){
16419                 conn.handleResponse(o, trans);
16420             };
16421
16422             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16423
16424             if(this.autoAbort !== false){
16425                 this.abort();
16426             }
16427
16428             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16429
16430             var script = document.createElement("script");
16431             script.setAttribute("src", url);
16432             script.setAttribute("type", "text/javascript");
16433             script.setAttribute("id", trans.scriptId);
16434             this.head.appendChild(script);
16435
16436             this.trans = trans;
16437         }else{
16438             callback.call(scope||this, null, arg, false);
16439         }
16440     },
16441
16442     // private
16443     isLoading : function(){
16444         return this.trans ? true : false;
16445     },
16446
16447     /**
16448      * Abort the current server request.
16449      */
16450     abort : function(){
16451         if(this.isLoading()){
16452             this.destroyTrans(this.trans);
16453         }
16454     },
16455
16456     // private
16457     destroyTrans : function(trans, isLoaded){
16458         this.head.removeChild(document.getElementById(trans.scriptId));
16459         clearTimeout(trans.timeoutId);
16460         if(isLoaded){
16461             window[trans.cb] = undefined;
16462             try{
16463                 delete window[trans.cb];
16464             }catch(e){}
16465         }else{
16466             // if hasn't been loaded, wait for load to remove it to prevent script error
16467             window[trans.cb] = function(){
16468                 window[trans.cb] = undefined;
16469                 try{
16470                     delete window[trans.cb];
16471                 }catch(e){}
16472             };
16473         }
16474     },
16475
16476     // private
16477     handleResponse : function(o, trans){
16478         this.trans = false;
16479         this.destroyTrans(trans, true);
16480         var result;
16481         try {
16482             result = trans.reader.readRecords(o);
16483         }catch(e){
16484             this.fireEvent("loadexception", this, o, trans.arg, e);
16485             trans.callback.call(trans.scope||window, null, trans.arg, false);
16486             return;
16487         }
16488         this.fireEvent("load", this, o, trans.arg);
16489         trans.callback.call(trans.scope||window, result, trans.arg, true);
16490     },
16491
16492     // private
16493     handleFailure : function(trans){
16494         this.trans = false;
16495         this.destroyTrans(trans, false);
16496         this.fireEvent("loadexception", this, null, trans.arg);
16497         trans.callback.call(trans.scope||window, null, trans.arg, false);
16498     }
16499 });/*
16500  * Based on:
16501  * Ext JS Library 1.1.1
16502  * Copyright(c) 2006-2007, Ext JS, LLC.
16503  *
16504  * Originally Released Under LGPL - original licence link has changed is not relivant.
16505  *
16506  * Fork - LGPL
16507  * <script type="text/javascript">
16508  */
16509
16510 /**
16511  * @class Roo.data.JsonReader
16512  * @extends Roo.data.DataReader
16513  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16514  * based on mappings in a provided Roo.data.Record constructor.
16515  * 
16516  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16517  * in the reply previously. 
16518  * 
16519  * <p>
16520  * Example code:
16521  * <pre><code>
16522 var RecordDef = Roo.data.Record.create([
16523     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16524     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16525 ]);
16526 var myReader = new Roo.data.JsonReader({
16527     totalProperty: "results",    // The property which contains the total dataset size (optional)
16528     root: "rows",                // The property which contains an Array of row objects
16529     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16530 }, RecordDef);
16531 </code></pre>
16532  * <p>
16533  * This would consume a JSON file like this:
16534  * <pre><code>
16535 { 'results': 2, 'rows': [
16536     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16537     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16538 }
16539 </code></pre>
16540  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16541  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16542  * paged from the remote server.
16543  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16544  * @cfg {String} root name of the property which contains the Array of row objects.
16545  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16546  * @cfg {Array} fields Array of field definition objects
16547  * @constructor
16548  * Create a new JsonReader
16549  * @param {Object} meta Metadata configuration options
16550  * @param {Object} recordType Either an Array of field definition objects,
16551  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16552  */
16553 Roo.data.JsonReader = function(meta, recordType){
16554     
16555     meta = meta || {};
16556     // set some defaults:
16557     Roo.applyIf(meta, {
16558         totalProperty: 'total',
16559         successProperty : 'success',
16560         root : 'data',
16561         id : 'id'
16562     });
16563     
16564     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16565 };
16566 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16567     
16568     readerType : 'Json',
16569     
16570     /**
16571      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16572      * Used by Store query builder to append _requestMeta to params.
16573      * 
16574      */
16575     metaFromRemote : false,
16576     /**
16577      * This method is only used by a DataProxy which has retrieved data from a remote server.
16578      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16579      * @return {Object} data A data block which is used by an Roo.data.Store object as
16580      * a cache of Roo.data.Records.
16581      */
16582     read : function(response){
16583         var json = response.responseText;
16584        
16585         var o = /* eval:var:o */ eval("("+json+")");
16586         if(!o) {
16587             throw {message: "JsonReader.read: Json object not found"};
16588         }
16589         
16590         if(o.metaData){
16591             
16592             delete this.ef;
16593             this.metaFromRemote = true;
16594             this.meta = o.metaData;
16595             this.recordType = Roo.data.Record.create(o.metaData.fields);
16596             this.onMetaChange(this.meta, this.recordType, o);
16597         }
16598         return this.readRecords(o);
16599     },
16600
16601     // private function a store will implement
16602     onMetaChange : function(meta, recordType, o){
16603
16604     },
16605
16606     /**
16607          * @ignore
16608          */
16609     simpleAccess: function(obj, subsc) {
16610         return obj[subsc];
16611     },
16612
16613         /**
16614          * @ignore
16615          */
16616     getJsonAccessor: function(){
16617         var re = /[\[\.]/;
16618         return function(expr) {
16619             try {
16620                 return(re.test(expr))
16621                     ? new Function("obj", "return obj." + expr)
16622                     : function(obj){
16623                         return obj[expr];
16624                     };
16625             } catch(e){}
16626             return Roo.emptyFn;
16627         };
16628     }(),
16629
16630     /**
16631      * Create a data block containing Roo.data.Records from an XML document.
16632      * @param {Object} o An object which contains an Array of row objects in the property specified
16633      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16634      * which contains the total size of the dataset.
16635      * @return {Object} data A data block which is used by an Roo.data.Store object as
16636      * a cache of Roo.data.Records.
16637      */
16638     readRecords : function(o){
16639         /**
16640          * After any data loads, the raw JSON data is available for further custom processing.
16641          * @type Object
16642          */
16643         this.o = o;
16644         var s = this.meta, Record = this.recordType,
16645             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16646
16647 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16648         if (!this.ef) {
16649             if(s.totalProperty) {
16650                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16651                 }
16652                 if(s.successProperty) {
16653                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16654                 }
16655                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16656                 if (s.id) {
16657                         var g = this.getJsonAccessor(s.id);
16658                         this.getId = function(rec) {
16659                                 var r = g(rec);  
16660                                 return (r === undefined || r === "") ? null : r;
16661                         };
16662                 } else {
16663                         this.getId = function(){return null;};
16664                 }
16665             this.ef = [];
16666             for(var jj = 0; jj < fl; jj++){
16667                 f = fi[jj];
16668                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16669                 this.ef[jj] = this.getJsonAccessor(map);
16670             }
16671         }
16672
16673         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16674         if(s.totalProperty){
16675             var vt = parseInt(this.getTotal(o), 10);
16676             if(!isNaN(vt)){
16677                 totalRecords = vt;
16678             }
16679         }
16680         if(s.successProperty){
16681             var vs = this.getSuccess(o);
16682             if(vs === false || vs === 'false'){
16683                 success = false;
16684             }
16685         }
16686         var records = [];
16687         for(var i = 0; i < c; i++){
16688             var n = root[i];
16689             var values = {};
16690             var id = this.getId(n);
16691             for(var j = 0; j < fl; j++){
16692                 f = fi[j];
16693                                 var v = this.ef[j](n);
16694                                 if (!f.convert) {
16695                                         Roo.log('missing convert for ' + f.name);
16696                                         Roo.log(f);
16697                                         continue;
16698                                 }
16699                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16700             }
16701                         if (!Record) {
16702                                 return {
16703                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16704                                         success : false,
16705                                         records : [],
16706                                         totalRecords : 0
16707                                 };
16708                         }
16709             var record = new Record(values, id);
16710             record.json = n;
16711             records[i] = record;
16712         }
16713         return {
16714             raw : o,
16715             success : success,
16716             records : records,
16717             totalRecords : totalRecords
16718         };
16719     },
16720     // used when loading children.. @see loadDataFromChildren
16721     toLoadData: function(rec)
16722     {
16723         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16724         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16725         return { data : data, total : data.length };
16726         
16727     }
16728 });/*
16729  * Based on:
16730  * Ext JS Library 1.1.1
16731  * Copyright(c) 2006-2007, Ext JS, LLC.
16732  *
16733  * Originally Released Under LGPL - original licence link has changed is not relivant.
16734  *
16735  * Fork - LGPL
16736  * <script type="text/javascript">
16737  */
16738
16739 /**
16740  * @class Roo.data.ArrayReader
16741  * @extends Roo.data.DataReader
16742  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16743  * Each element of that Array represents a row of data fields. The
16744  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16745  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16746  * <p>
16747  * Example code:.
16748  * <pre><code>
16749 var RecordDef = Roo.data.Record.create([
16750     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16751     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16752 ]);
16753 var myReader = new Roo.data.ArrayReader({
16754     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16755 }, RecordDef);
16756 </code></pre>
16757  * <p>
16758  * This would consume an Array like this:
16759  * <pre><code>
16760 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16761   </code></pre>
16762  
16763  * @constructor
16764  * Create a new JsonReader
16765  * @param {Object} meta Metadata configuration options.
16766  * @param {Object|Array} recordType Either an Array of field definition objects
16767  * 
16768  * @cfg {Array} fields Array of field definition objects
16769  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16770  * as specified to {@link Roo.data.Record#create},
16771  * or an {@link Roo.data.Record} object
16772  *
16773  * 
16774  * created using {@link Roo.data.Record#create}.
16775  */
16776 Roo.data.ArrayReader = function(meta, recordType)
16777 {    
16778     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16779 };
16780
16781 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16782     
16783       /**
16784      * Create a data block containing Roo.data.Records from an XML document.
16785      * @param {Object} o An Array of row objects which represents the dataset.
16786      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16787      * a cache of Roo.data.Records.
16788      */
16789     readRecords : function(o)
16790     {
16791         var sid = this.meta ? this.meta.id : null;
16792         var recordType = this.recordType, fields = recordType.prototype.fields;
16793         var records = [];
16794         var root = o;
16795         for(var i = 0; i < root.length; i++){
16796             var n = root[i];
16797             var values = {};
16798             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16799             for(var j = 0, jlen = fields.length; j < jlen; j++){
16800                 var f = fields.items[j];
16801                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16802                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16803                 v = f.convert(v);
16804                 values[f.name] = v;
16805             }
16806             var record = new recordType(values, id);
16807             record.json = n;
16808             records[records.length] = record;
16809         }
16810         return {
16811             records : records,
16812             totalRecords : records.length
16813         };
16814     },
16815     // used when loading children.. @see loadDataFromChildren
16816     toLoadData: function(rec)
16817     {
16818         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16819         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16820         
16821     }
16822     
16823     
16824 });/*
16825  * - LGPL
16826  * * 
16827  */
16828
16829 /**
16830  * @class Roo.bootstrap.form.ComboBox
16831  * @extends Roo.bootstrap.form.TriggerField
16832  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16833  * @cfg {Boolean} append (true|false) default false
16834  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16835  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16836  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16837  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16838  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16839  * @cfg {Boolean} animate default true
16840  * @cfg {Boolean} emptyResultText only for touch device
16841  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16842  * @cfg {String} emptyTitle default ''
16843  * @cfg {Number} width fixed with? experimental
16844  * @constructor
16845  * Create a new ComboBox.
16846  * @param {Object} config Configuration options
16847  */
16848 Roo.bootstrap.form.ComboBox = function(config){
16849     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16850     this.addEvents({
16851         /**
16852          * @event expand
16853          * Fires when the dropdown list is expanded
16854         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16855         */
16856         'expand' : true,
16857         /**
16858          * @event collapse
16859          * Fires when the dropdown list is collapsed
16860         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861         */
16862         'collapse' : true,
16863         /**
16864          * @event beforeselect
16865          * Fires before a list item is selected. Return false to cancel the selection.
16866         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867         * @param {Roo.data.Record} record The data record returned from the underlying store
16868         * @param {Number} index The index of the selected item in the dropdown list
16869         */
16870         'beforeselect' : true,
16871         /**
16872          * @event select
16873          * Fires when a list item is selected
16874         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16876         * @param {Number} index The index of the selected item in the dropdown list
16877         */
16878         'select' : true,
16879         /**
16880          * @event beforequery
16881          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16882          * The event object passed has these properties:
16883         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16884         * @param {String} query The query
16885         * @param {Boolean} forceAll true to force "all" query
16886         * @param {Boolean} cancel true to cancel the query
16887         * @param {Object} e The query event object
16888         */
16889         'beforequery': true,
16890          /**
16891          * @event add
16892          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16893         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894         */
16895         'add' : true,
16896         /**
16897          * @event edit
16898          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16899         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16900         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16901         */
16902         'edit' : true,
16903         /**
16904          * @event remove
16905          * Fires when the remove value from the combobox array
16906         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16907         */
16908         'remove' : true,
16909         /**
16910          * @event afterremove
16911          * Fires when the remove value from the combobox array
16912         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913         */
16914         'afterremove' : true,
16915         /**
16916          * @event specialfilter
16917          * Fires when specialfilter
16918             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16919             */
16920         'specialfilter' : true,
16921         /**
16922          * @event tick
16923          * Fires when tick the element
16924             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16925             */
16926         'tick' : true,
16927         /**
16928          * @event touchviewdisplay
16929          * Fires when touch view require special display (default is using displayField)
16930             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16931             * @param {Object} cfg set html .
16932             */
16933         'touchviewdisplay' : true
16934         
16935     });
16936     
16937     this.item = [];
16938     this.tickItems = [];
16939     
16940     this.selectedIndex = -1;
16941     if(this.mode == 'local'){
16942         if(config.queryDelay === undefined){
16943             this.queryDelay = 10;
16944         }
16945         if(config.minChars === undefined){
16946             this.minChars = 0;
16947         }
16948     }
16949 };
16950
16951 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16952      
16953     /**
16954      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16955      * rendering into an Roo.Editor, defaults to false)
16956      */
16957     /**
16958      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16959      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16960      */
16961     /**
16962      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16963      */
16964     /**
16965      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16966      * the dropdown list (defaults to undefined, with no header element)
16967      */
16968
16969      /**
16970      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16971      */
16972      
16973      /**
16974      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16975      */
16976     listWidth: undefined,
16977     /**
16978      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16979      * mode = 'remote' or 'text' if mode = 'local')
16980      */
16981     displayField: undefined,
16982     
16983     /**
16984      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16985      * mode = 'remote' or 'value' if mode = 'local'). 
16986      * Note: use of a valueField requires the user make a selection
16987      * in order for a value to be mapped.
16988      */
16989     valueField: undefined,
16990     /**
16991      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16992      */
16993     modalTitle : '',
16994     
16995     /**
16996      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16997      * field's data value (defaults to the underlying DOM element's name)
16998      */
16999     hiddenName: undefined,
17000     /**
17001      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17002      */
17003     listClass: '',
17004     /**
17005      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17006      */
17007     selectedClass: 'active',
17008     
17009     /**
17010      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17011      */
17012     shadow:'sides',
17013     /**
17014      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17015      * anchor positions (defaults to 'tl-bl')
17016      */
17017     listAlign: 'tl-bl?',
17018     /**
17019      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17020      */
17021     maxHeight: 300,
17022     /**
17023      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17024      * query specified by the allQuery config option (defaults to 'query')
17025      */
17026     triggerAction: 'query',
17027     /**
17028      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17029      * (defaults to 4, does not apply if editable = false)
17030      */
17031     minChars : 4,
17032     /**
17033      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17034      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17035      */
17036     typeAhead: false,
17037     /**
17038      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17039      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17040      */
17041     queryDelay: 500,
17042     /**
17043      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17044      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17045      */
17046     pageSize: 0,
17047     /**
17048      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17049      * when editable = true (defaults to false)
17050      */
17051     selectOnFocus:false,
17052     /**
17053      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17054      */
17055     queryParam: 'query',
17056     /**
17057      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17058      * when mode = 'remote' (defaults to 'Loading...')
17059      */
17060     loadingText: 'Loading...',
17061     /**
17062      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17063      */
17064     resizable: false,
17065     /**
17066      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17067      */
17068     handleHeight : 8,
17069     /**
17070      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17071      * traditional select (defaults to true)
17072      */
17073     editable: true,
17074     /**
17075      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17076      */
17077     allQuery: '',
17078     /**
17079      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17080      */
17081     mode: 'remote',
17082     /**
17083      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17084      * listWidth has a higher value)
17085      */
17086     minListWidth : 70,
17087     /**
17088      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17089      * allow the user to set arbitrary text into the field (defaults to false)
17090      */
17091     forceSelection:false,
17092     /**
17093      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17094      * if typeAhead = true (defaults to 250)
17095      */
17096     typeAheadDelay : 250,
17097     /**
17098      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17099      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17100      */
17101     valueNotFoundText : undefined,
17102     /**
17103      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17104      */
17105     blockFocus : false,
17106     
17107     /**
17108      * @cfg {Boolean} disableClear Disable showing of clear button.
17109      */
17110     disableClear : false,
17111     /**
17112      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17113      */
17114     alwaysQuery : false,
17115     
17116     /**
17117      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17118      */
17119     multiple : false,
17120     
17121     /**
17122      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17123      */
17124     invalidClass : "has-warning",
17125     
17126     /**
17127      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17128      */
17129     validClass : "has-success",
17130     
17131     /**
17132      * @cfg {Boolean} specialFilter (true|false) special filter default false
17133      */
17134     specialFilter : false,
17135     
17136     /**
17137      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17138      */
17139     mobileTouchView : true,
17140     
17141     /**
17142      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17143      */
17144     useNativeIOS : false,
17145     
17146     /**
17147      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17148      */
17149     mobile_restrict_height : false,
17150     
17151     ios_options : false,
17152     
17153     //private
17154     addicon : false,
17155     editicon: false,
17156     
17157     page: 0,
17158     hasQuery: false,
17159     append: false,
17160     loadNext: false,
17161     autoFocus : true,
17162     tickable : false,
17163     btnPosition : 'right',
17164     triggerList : true,
17165     showToggleBtn : true,
17166     animate : true,
17167     emptyResultText: 'Empty',
17168     triggerText : 'Select',
17169     emptyTitle : '',
17170     width : false,
17171     
17172     // element that contains real text value.. (when hidden is used..)
17173     
17174     getAutoCreate : function()
17175     {   
17176         var cfg = false;
17177         //render
17178         /*
17179          * Render classic select for iso
17180          */
17181         
17182         if(Roo.isIOS && this.useNativeIOS){
17183             cfg = this.getAutoCreateNativeIOS();
17184             return cfg;
17185         }
17186         
17187         /*
17188          * Touch Devices
17189          */
17190         
17191         if(Roo.isTouch && this.mobileTouchView){
17192             cfg = this.getAutoCreateTouchView();
17193             return cfg;;
17194         }
17195         
17196         /*
17197          *  Normal ComboBox
17198          */
17199         if(!this.tickable){
17200             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17201             return cfg;
17202         }
17203         
17204         /*
17205          *  ComboBox with tickable selections
17206          */
17207              
17208         var align = this.labelAlign || this.parentLabelAlign();
17209         
17210         cfg = {
17211             cls : 'form-group roo-combobox-tickable' //input-group
17212         };
17213         
17214         var btn_text_select = '';
17215         var btn_text_done = '';
17216         var btn_text_cancel = '';
17217         
17218         if (this.btn_text_show) {
17219             btn_text_select = 'Select';
17220             btn_text_done = 'Done';
17221             btn_text_cancel = 'Cancel'; 
17222         }
17223         
17224         var buttons = {
17225             tag : 'div',
17226             cls : 'tickable-buttons',
17227             cn : [
17228                 {
17229                     tag : 'button',
17230                     type : 'button',
17231                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17232                     //html : this.triggerText
17233                     html: btn_text_select
17234                 },
17235                 {
17236                     tag : 'button',
17237                     type : 'button',
17238                     name : 'ok',
17239                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17240                     //html : 'Done'
17241                     html: btn_text_done
17242                 },
17243                 {
17244                     tag : 'button',
17245                     type : 'button',
17246                     name : 'cancel',
17247                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17248                     //html : 'Cancel'
17249                     html: btn_text_cancel
17250                 }
17251             ]
17252         };
17253         
17254         if(this.editable){
17255             buttons.cn.unshift({
17256                 tag: 'input',
17257                 cls: 'roo-select2-search-field-input'
17258             });
17259         }
17260         
17261         var _this = this;
17262         
17263         Roo.each(buttons.cn, function(c){
17264             if (_this.size) {
17265                 c.cls += ' btn-' + _this.size;
17266             }
17267
17268             if (_this.disabled) {
17269                 c.disabled = true;
17270             }
17271         });
17272         
17273         var box = {
17274             tag: 'div',
17275             style : 'display: contents',
17276             cn: [
17277                 {
17278                     tag: 'input',
17279                     type : 'hidden',
17280                     cls: 'form-hidden-field'
17281                 },
17282                 {
17283                     tag: 'ul',
17284                     cls: 'roo-select2-choices',
17285                     cn:[
17286                         {
17287                             tag: 'li',
17288                             cls: 'roo-select2-search-field',
17289                             cn: [
17290                                 buttons
17291                             ]
17292                         }
17293                     ]
17294                 }
17295             ]
17296         };
17297         
17298         var combobox = {
17299             cls: 'roo-select2-container input-group roo-select2-container-multi',
17300             cn: [
17301                 
17302                 box
17303 //                {
17304 //                    tag: 'ul',
17305 //                    cls: 'typeahead typeahead-long dropdown-menu',
17306 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17307 //                }
17308             ]
17309         };
17310         
17311         if(this.hasFeedback && !this.allowBlank){
17312             
17313             var feedback = {
17314                 tag: 'span',
17315                 cls: 'glyphicon form-control-feedback'
17316             };
17317
17318             combobox.cn.push(feedback);
17319         }
17320         
17321         
17322         
17323         var indicator = {
17324             tag : 'i',
17325             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17326             tooltip : 'This field is required'
17327         };
17328          
17329         if (this.allowBlank) {
17330             indicator = {
17331                 tag : 'i',
17332                 style : 'display:none'
17333             };
17334         } 
17335         if (align ==='left' && this.fieldLabel.length) {
17336             
17337             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17338             
17339             cfg.cn = [
17340                 indicator,
17341                 {
17342                     tag: 'label',
17343                     'for' :  id,
17344                     cls : 'control-label col-form-label',
17345                     html : this.fieldLabel
17346
17347                 },
17348                 {
17349                     cls : "", 
17350                     cn: [
17351                         combobox
17352                     ]
17353                 }
17354
17355             ];
17356             
17357             var labelCfg = cfg.cn[1];
17358             var contentCfg = cfg.cn[2];
17359             
17360
17361             if(this.indicatorpos == 'right'){
17362                 
17363                 cfg.cn = [
17364                     {
17365                         tag: 'label',
17366                         'for' :  id,
17367                         cls : 'control-label col-form-label',
17368                         cn : [
17369                             {
17370                                 tag : 'span',
17371                                 html : this.fieldLabel
17372                             },
17373                             indicator
17374                         ]
17375                     },
17376                     {
17377                         cls : "",
17378                         cn: [
17379                             combobox
17380                         ]
17381                     }
17382
17383                 ];
17384                 
17385                 
17386                 
17387                 labelCfg = cfg.cn[0];
17388                 contentCfg = cfg.cn[1];
17389             
17390             }
17391             
17392             if(this.labelWidth > 12){
17393                 labelCfg.style = "width: " + this.labelWidth + 'px';
17394             }
17395             if(this.width * 1 > 0){
17396                 contentCfg.style = "width: " + this.width + 'px';
17397             }
17398             if(this.labelWidth < 13 && this.labelmd == 0){
17399                 this.labelmd = this.labelWidth;
17400             }
17401             
17402             if(this.labellg > 0){
17403                 labelCfg.cls += ' col-lg-' + this.labellg;
17404                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17405             }
17406             
17407             if(this.labelmd > 0){
17408                 labelCfg.cls += ' col-md-' + this.labelmd;
17409                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17410             }
17411             
17412             if(this.labelsm > 0){
17413                 labelCfg.cls += ' col-sm-' + this.labelsm;
17414                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17415             }
17416             
17417             if(this.labelxs > 0){
17418                 labelCfg.cls += ' col-xs-' + this.labelxs;
17419                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17420             }
17421                 
17422                 
17423         } else if ( this.fieldLabel.length) {
17424 //                Roo.log(" label");
17425                  cfg.cn = [
17426                    indicator,
17427                     {
17428                         tag: 'label',
17429                         //cls : 'input-group-addon',
17430                         html : this.fieldLabel
17431                     },
17432                     combobox
17433                 ];
17434                 
17435                 if(this.indicatorpos == 'right'){
17436                     cfg.cn = [
17437                         {
17438                             tag: 'label',
17439                             //cls : 'input-group-addon',
17440                             html : this.fieldLabel
17441                         },
17442                         indicator,
17443                         combobox
17444                     ];
17445                     
17446                 }
17447
17448         } else {
17449             
17450 //                Roo.log(" no label && no align");
17451                 cfg = combobox
17452                      
17453                 
17454         }
17455          
17456         var settings=this;
17457         ['xs','sm','md','lg'].map(function(size){
17458             if (settings[size]) {
17459                 cfg.cls += ' col-' + size + '-' + settings[size];
17460             }
17461         });
17462         
17463         return cfg;
17464         
17465     },
17466     
17467     _initEventsCalled : false,
17468     
17469     // private
17470     initEvents: function()
17471     {   
17472         if (this._initEventsCalled) { // as we call render... prevent looping...
17473             return;
17474         }
17475         this._initEventsCalled = true;
17476         
17477         if (!this.store) {
17478             throw "can not find store for combo";
17479         }
17480         
17481         this.indicator = this.indicatorEl();
17482         
17483         this.store = Roo.factory(this.store, Roo.data);
17484         this.store.parent = this;
17485         
17486         // if we are building from html. then this element is so complex, that we can not really
17487         // use the rendered HTML.
17488         // so we have to trash and replace the previous code.
17489         if (Roo.XComponent.build_from_html) {
17490             // remove this element....
17491             var e = this.el.dom, k=0;
17492             while (e ) { e = e.previousSibling;  ++k;}
17493
17494             this.el.remove();
17495             
17496             this.el=false;
17497             this.rendered = false;
17498             
17499             this.render(this.parent().getChildContainer(true), k);
17500         }
17501         
17502         if(Roo.isIOS && this.useNativeIOS){
17503             this.initIOSView();
17504             return;
17505         }
17506         
17507         /*
17508          * Touch Devices
17509          */
17510         
17511         if(Roo.isTouch && this.mobileTouchView){
17512             this.initTouchView();
17513             return;
17514         }
17515         
17516         if(this.tickable){
17517             this.initTickableEvents();
17518             return;
17519         }
17520         
17521         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17522         
17523         if(this.hiddenName){
17524             
17525             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17526             
17527             this.hiddenField.dom.value =
17528                 this.hiddenValue !== undefined ? this.hiddenValue :
17529                 this.value !== undefined ? this.value : '';
17530
17531             // prevent input submission
17532             this.el.dom.removeAttribute('name');
17533             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17534              
17535              
17536         }
17537         //if(Roo.isGecko){
17538         //    this.el.dom.setAttribute('autocomplete', 'off');
17539         //}
17540         
17541         var cls = 'x-combo-list';
17542         
17543         //this.list = new Roo.Layer({
17544         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17545         //});
17546         
17547         var _this = this;
17548         
17549         (function(){
17550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17551             _this.list.setWidth(lw);
17552         }).defer(100);
17553         
17554         this.list.on('mouseover', this.onViewOver, this);
17555         this.list.on('mousemove', this.onViewMove, this);
17556         this.list.on('scroll', this.onViewScroll, this);
17557         
17558         /*
17559         this.list.swallowEvent('mousewheel');
17560         this.assetHeight = 0;
17561
17562         if(this.title){
17563             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17564             this.assetHeight += this.header.getHeight();
17565         }
17566
17567         this.innerList = this.list.createChild({cls:cls+'-inner'});
17568         this.innerList.on('mouseover', this.onViewOver, this);
17569         this.innerList.on('mousemove', this.onViewMove, this);
17570         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17571         
17572         if(this.allowBlank && !this.pageSize && !this.disableClear){
17573             this.footer = this.list.createChild({cls:cls+'-ft'});
17574             this.pageTb = new Roo.Toolbar(this.footer);
17575            
17576         }
17577         if(this.pageSize){
17578             this.footer = this.list.createChild({cls:cls+'-ft'});
17579             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17580                     {pageSize: this.pageSize});
17581             
17582         }
17583         
17584         if (this.pageTb && this.allowBlank && !this.disableClear) {
17585             var _this = this;
17586             this.pageTb.add(new Roo.Toolbar.Fill(), {
17587                 cls: 'x-btn-icon x-btn-clear',
17588                 text: '&#160;',
17589                 handler: function()
17590                 {
17591                     _this.collapse();
17592                     _this.clearValue();
17593                     _this.onSelect(false, -1);
17594                 }
17595             });
17596         }
17597         if (this.footer) {
17598             this.assetHeight += this.footer.getHeight();
17599         }
17600         */
17601             
17602         if(!this.tpl){
17603             this.tpl = Roo.bootstrap.version == 4 ?
17604                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17605                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17606         }
17607
17608         this.view = new Roo.View(this.list, this.tpl, {
17609             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17610         });
17611         //this.view.wrapEl.setDisplayed(false);
17612         this.view.on('click', this.onViewClick, this);
17613         
17614         
17615         this.store.on('beforeload', this.onBeforeLoad, this);
17616         this.store.on('load', this.onLoad, this);
17617         this.store.on('loadexception', this.onLoadException, this);
17618         /*
17619         if(this.resizable){
17620             this.resizer = new Roo.Resizable(this.list,  {
17621                pinned:true, handles:'se'
17622             });
17623             this.resizer.on('resize', function(r, w, h){
17624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17625                 this.listWidth = w;
17626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17627                 this.restrictHeight();
17628             }, this);
17629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17630         }
17631         */
17632         if(!this.editable){
17633             this.editable = true;
17634             this.setEditable(false);
17635         }
17636         
17637         /*
17638         
17639         if (typeof(this.events.add.listeners) != 'undefined') {
17640             
17641             this.addicon = this.wrap.createChild(
17642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17643        
17644             this.addicon.on('click', function(e) {
17645                 this.fireEvent('add', this);
17646             }, this);
17647         }
17648         if (typeof(this.events.edit.listeners) != 'undefined') {
17649             
17650             this.editicon = this.wrap.createChild(
17651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17652             if (this.addicon) {
17653                 this.editicon.setStyle('margin-left', '40px');
17654             }
17655             this.editicon.on('click', function(e) {
17656                 
17657                 // we fire even  if inothing is selected..
17658                 this.fireEvent('edit', this, this.lastData );
17659                 
17660             }, this);
17661         }
17662         */
17663         
17664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17665             "up" : function(e){
17666                 this.inKeyMode = true;
17667                 this.selectPrev();
17668             },
17669
17670             "down" : function(e){
17671                 if(!this.isExpanded()){
17672                     this.onTriggerClick();
17673                 }else{
17674                     this.inKeyMode = true;
17675                     this.selectNext();
17676                 }
17677             },
17678
17679             "enter" : function(e){
17680 //                this.onViewClick();
17681                 //return true;
17682                 this.collapse();
17683                 
17684                 if(this.fireEvent("specialkey", this, e)){
17685                     this.onViewClick(false);
17686                 }
17687                 
17688                 return true;
17689             },
17690
17691             "esc" : function(e){
17692                 this.collapse();
17693             },
17694
17695             "tab" : function(e){
17696                 this.collapse();
17697                 
17698                 if(this.fireEvent("specialkey", this, e)){
17699                     this.onViewClick(false);
17700                 }
17701                 
17702                 return true;
17703             },
17704
17705             scope : this,
17706
17707             doRelay : function(foo, bar, hname){
17708                 if(hname == 'down' || this.scope.isExpanded()){
17709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17710                 }
17711                 return true;
17712             },
17713
17714             forceKeyDown: true
17715         });
17716         
17717         
17718         this.queryDelay = Math.max(this.queryDelay || 10,
17719                 this.mode == 'local' ? 10 : 250);
17720         
17721         
17722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17723         
17724         if(this.typeAhead){
17725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17726         }
17727         if(this.editable !== false){
17728             this.inputEl().on("keyup", this.onKeyUp, this);
17729         }
17730         if(this.forceSelection){
17731             this.inputEl().on('blur', this.doForce, this);
17732         }
17733         
17734         if(this.multiple){
17735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17737         }
17738     },
17739     
17740     initTickableEvents: function()
17741     {   
17742         this.createList();
17743         
17744         if(this.hiddenName){
17745             
17746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17747             
17748             this.hiddenField.dom.value =
17749                 this.hiddenValue !== undefined ? this.hiddenValue :
17750                 this.value !== undefined ? this.value : '';
17751
17752             // prevent input submission
17753             this.el.dom.removeAttribute('name');
17754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17755              
17756              
17757         }
17758         
17759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17760         
17761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17763         if(this.triggerList){
17764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17765         }
17766          
17767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17769         
17770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17772         
17773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17775         
17776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         
17780         this.okBtn.hide();
17781         this.cancelBtn.hide();
17782         
17783         var _this = this;
17784         
17785         (function(){
17786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17787             _this.list.setWidth(lw);
17788         }).defer(100);
17789         
17790         this.list.on('mouseover', this.onViewOver, this);
17791         this.list.on('mousemove', this.onViewMove, this);
17792         
17793         this.list.on('scroll', this.onViewScroll, this);
17794         
17795         if(!this.tpl){
17796             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17797                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17798         }
17799
17800         this.view = new Roo.View(this.list, this.tpl, {
17801             singleSelect:true,
17802             tickable:true,
17803             parent:this,
17804             store: this.store,
17805             selectedClass: this.selectedClass
17806         });
17807         
17808         //this.view.wrapEl.setDisplayed(false);
17809         this.view.on('click', this.onViewClick, this);
17810         
17811         
17812         
17813         this.store.on('beforeload', this.onBeforeLoad, this);
17814         this.store.on('load', this.onLoad, this);
17815         this.store.on('loadexception', this.onLoadException, this);
17816         
17817         if(this.editable){
17818             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17819                 "up" : function(e){
17820                     this.inKeyMode = true;
17821                     this.selectPrev();
17822                 },
17823
17824                 "down" : function(e){
17825                     this.inKeyMode = true;
17826                     this.selectNext();
17827                 },
17828
17829                 "enter" : function(e){
17830                     if(this.fireEvent("specialkey", this, e)){
17831                         this.onViewClick(false);
17832                     }
17833                     
17834                     return true;
17835                 },
17836
17837                 "esc" : function(e){
17838                     this.onTickableFooterButtonClick(e, false, false);
17839                 },
17840
17841                 "tab" : function(e){
17842                     this.fireEvent("specialkey", this, e);
17843                     
17844                     this.onTickableFooterButtonClick(e, false, false);
17845                     
17846                     return true;
17847                 },
17848
17849                 scope : this,
17850
17851                 doRelay : function(e, fn, key){
17852                     if(this.scope.isExpanded()){
17853                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17854                     }
17855                     return true;
17856                 },
17857
17858                 forceKeyDown: true
17859             });
17860         }
17861         
17862         this.queryDelay = Math.max(this.queryDelay || 10,
17863                 this.mode == 'local' ? 10 : 250);
17864         
17865         
17866         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17867         
17868         if(this.typeAhead){
17869             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17870         }
17871         
17872         if(this.editable !== false){
17873             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17874         }
17875         
17876         this.indicator = this.indicatorEl();
17877         
17878         if(this.indicator){
17879             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17880             this.indicator.hide();
17881         }
17882         
17883     },
17884
17885     onDestroy : function(){
17886         if(this.view){
17887             this.view.setStore(null);
17888             this.view.el.removeAllListeners();
17889             this.view.el.remove();
17890             this.view.purgeListeners();
17891         }
17892         if(this.list){
17893             this.list.dom.innerHTML  = '';
17894         }
17895         
17896         if(this.store){
17897             this.store.un('beforeload', this.onBeforeLoad, this);
17898             this.store.un('load', this.onLoad, this);
17899             this.store.un('loadexception', this.onLoadException, this);
17900         }
17901         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17902     },
17903
17904     // private
17905     fireKey : function(e){
17906         if(e.isNavKeyPress() && !this.list.isVisible()){
17907             this.fireEvent("specialkey", this, e);
17908         }
17909     },
17910
17911     // private
17912     onResize: function(w, h)
17913     {
17914         
17915         
17916 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17917 //        
17918 //        if(typeof w != 'number'){
17919 //            // we do not handle it!?!?
17920 //            return;
17921 //        }
17922 //        var tw = this.trigger.getWidth();
17923 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17924 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17925 //        var x = w - tw;
17926 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17927 //            
17928 //        //this.trigger.setStyle('left', x+'px');
17929 //        
17930 //        if(this.list && this.listWidth === undefined){
17931 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17932 //            this.list.setWidth(lw);
17933 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17934 //        }
17935         
17936     
17937         
17938     },
17939
17940     /**
17941      * Allow or prevent the user from directly editing the field text.  If false is passed,
17942      * the user will only be able to select from the items defined in the dropdown list.  This method
17943      * is the runtime equivalent of setting the 'editable' config option at config time.
17944      * @param {Boolean} value True to allow the user to directly edit the field text
17945      */
17946     setEditable : function(value){
17947         if(value == this.editable){
17948             return;
17949         }
17950         this.editable = value;
17951         if(!value){
17952             this.inputEl().dom.setAttribute('readOnly', true);
17953             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17954             this.inputEl().addClass('x-combo-noedit');
17955         }else{
17956             this.inputEl().dom.removeAttribute('readOnly');
17957             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17958             this.inputEl().removeClass('x-combo-noedit');
17959         }
17960     },
17961
17962     // private
17963     
17964     onBeforeLoad : function(combo,opts){
17965         if(!this.hasFocus){
17966             return;
17967         }
17968          if (!opts.add) {
17969             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17970          }
17971         this.restrictHeight();
17972         this.selectedIndex = -1;
17973     },
17974
17975     // private
17976     onLoad : function(){
17977         
17978         this.hasQuery = false;
17979         
17980         if(!this.hasFocus){
17981             return;
17982         }
17983         
17984         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17985             this.loading.hide();
17986         }
17987         
17988         if(this.store.getCount() > 0){
17989             
17990             this.expand();
17991             this.restrictHeight();
17992             if(this.lastQuery == this.allQuery){
17993                 if(this.editable && !this.tickable){
17994                     this.inputEl().dom.select();
17995                 }
17996                 
17997                 if(
17998                     !this.selectByValue(this.value, true) &&
17999                     this.autoFocus && 
18000                     (
18001                         !this.store.lastOptions ||
18002                         typeof(this.store.lastOptions.add) == 'undefined' || 
18003                         this.store.lastOptions.add != true
18004                     )
18005                 ){
18006                     this.select(0, true);
18007                 }
18008             }else{
18009                 if(this.autoFocus){
18010                     this.selectNext();
18011                 }
18012                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18013                     this.taTask.delay(this.typeAheadDelay);
18014                 }
18015             }
18016         }else{
18017             this.onEmptyResults();
18018         }
18019         
18020         //this.el.focus();
18021     },
18022     // private
18023     onLoadException : function()
18024     {
18025         this.hasQuery = false;
18026         
18027         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18028             this.loading.hide();
18029         }
18030         
18031         if(this.tickable && this.editable){
18032             return;
18033         }
18034         
18035         this.collapse();
18036         // only causes errors at present
18037         //Roo.log(this.store.reader.jsonData);
18038         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18039             // fixme
18040             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18041         //}
18042         
18043         
18044     },
18045     // private
18046     onTypeAhead : function(){
18047         if(this.store.getCount() > 0){
18048             var r = this.store.getAt(0);
18049             var newValue = r.data[this.displayField];
18050             var len = newValue.length;
18051             var selStart = this.getRawValue().length;
18052             
18053             if(selStart != len){
18054                 this.setRawValue(newValue);
18055                 this.selectText(selStart, newValue.length);
18056             }
18057         }
18058     },
18059
18060     // private
18061     onSelect : function(record, index){
18062         
18063         if(this.fireEvent('beforeselect', this, record, index) !== false){
18064         
18065             this.setFromData(index > -1 ? record.data : false);
18066             
18067             this.collapse();
18068             this.fireEvent('select', this, record, index);
18069         }
18070     },
18071
18072     /**
18073      * Returns the currently selected field value or empty string if no value is set.
18074      * @return {String} value The selected value
18075      */
18076     getValue : function()
18077     {
18078         if(Roo.isIOS && this.useNativeIOS){
18079             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18080         }
18081         
18082         if(this.multiple){
18083             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18084         }
18085         
18086         if(this.valueField){
18087             return typeof this.value != 'undefined' ? this.value : '';
18088         }else{
18089             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18090         }
18091     },
18092     
18093     getRawValue : function()
18094     {
18095         if(Roo.isIOS && this.useNativeIOS){
18096             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18097         }
18098         
18099         var v = this.inputEl().getValue();
18100         
18101         return v;
18102     },
18103
18104     /**
18105      * Clears any text/value currently set in the field
18106      */
18107     clearValue : function(){
18108         
18109         if(this.hiddenField){
18110             this.hiddenField.dom.value = '';
18111         }
18112         this.value = '';
18113         this.setRawValue('');
18114         this.lastSelectionText = '';
18115         this.lastData = false;
18116         
18117         var close = this.closeTriggerEl();
18118         
18119         if(close){
18120             close.hide();
18121         }
18122         
18123         this.validate();
18124         
18125     },
18126
18127     /**
18128      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18129      * will be displayed in the field.  If the value does not match the data value of an existing item,
18130      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18131      * Otherwise the field will be blank (although the value will still be set).
18132      * @param {String} value The value to match
18133      */
18134     setValue : function(v)
18135     {
18136         if(Roo.isIOS && this.useNativeIOS){
18137             this.setIOSValue(v);
18138             return;
18139         }
18140         
18141         if(this.multiple){
18142             this.syncValue();
18143             return;
18144         }
18145         
18146         var text = v;
18147         if(this.valueField){
18148             var r = this.findRecord(this.valueField, v);
18149             if(r){
18150                 text = r.data[this.displayField];
18151             }else if(this.valueNotFoundText !== undefined){
18152                 text = this.valueNotFoundText;
18153             }
18154         }
18155         this.lastSelectionText = text;
18156         if(this.hiddenField){
18157             this.hiddenField.dom.value = v;
18158         }
18159         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18160         this.value = v;
18161         
18162         var close = this.closeTriggerEl();
18163         
18164         if(close){
18165             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18166         }
18167         
18168         this.validate();
18169     },
18170     /**
18171      * @property {Object} the last set data for the element
18172      */
18173     
18174     lastData : false,
18175     /**
18176      * Sets the value of the field based on a object which is related to the record format for the store.
18177      * @param {Object} value the value to set as. or false on reset?
18178      */
18179     setFromData : function(o){
18180         
18181         if(this.multiple){
18182             this.addItem(o);
18183             return;
18184         }
18185             
18186         var dv = ''; // display value
18187         var vv = ''; // value value..
18188         this.lastData = o;
18189         if (this.displayField) {
18190             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18191         } else {
18192             // this is an error condition!!!
18193             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18194         }
18195         
18196         if(this.valueField){
18197             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18198         }
18199         
18200         var close = this.closeTriggerEl();
18201         
18202         if(close){
18203             if(dv.length || vv * 1 > 0){
18204                 close.show() ;
18205                 this.blockFocus=true;
18206             } else {
18207                 close.hide();
18208             }             
18209         }
18210         
18211         if(this.hiddenField){
18212             this.hiddenField.dom.value = vv;
18213             
18214             this.lastSelectionText = dv;
18215             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18216             this.value = vv;
18217             return;
18218         }
18219         // no hidden field.. - we store the value in 'value', but still display
18220         // display field!!!!
18221         this.lastSelectionText = dv;
18222         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18223         this.value = vv;
18224         
18225         
18226         
18227     },
18228     // private
18229     reset : function(){
18230         // overridden so that last data is reset..
18231         
18232         if(this.multiple){
18233             this.clearItem();
18234             return;
18235         }
18236         
18237         this.setValue(this.originalValue);
18238         //this.clearInvalid();
18239         this.lastData = false;
18240         if (this.view) {
18241             this.view.clearSelections();
18242         }
18243         
18244         this.validate();
18245     },
18246     // private
18247     findRecord : function(prop, value){
18248         var record;
18249         if(this.store.getCount() > 0){
18250             this.store.each(function(r){
18251                 if(r.data[prop] == value){
18252                     record = r;
18253                     return false;
18254                 }
18255                 return true;
18256             });
18257         }
18258         return record;
18259     },
18260     
18261     getName: function()
18262     {
18263         // returns hidden if it's set..
18264         if (!this.rendered) {return ''};
18265         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18266         
18267     },
18268     // private
18269     onViewMove : function(e, t){
18270         this.inKeyMode = false;
18271     },
18272
18273     // private
18274     onViewOver : function(e, t){
18275         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18276             return;
18277         }
18278         var item = this.view.findItemFromChild(t);
18279         
18280         if(item){
18281             var index = this.view.indexOf(item);
18282             this.select(index, false);
18283         }
18284     },
18285
18286     // private
18287     onViewClick : function(view, doFocus, el, e)
18288     {
18289         var index = this.view.getSelectedIndexes()[0];
18290         
18291         var r = this.store.getAt(index);
18292         
18293         if(this.tickable){
18294             
18295             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18296                 return;
18297             }
18298             
18299             var rm = false;
18300             var _this = this;
18301             
18302             Roo.each(this.tickItems, function(v,k){
18303                 
18304                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18305                     Roo.log(v);
18306                     _this.tickItems.splice(k, 1);
18307                     
18308                     if(typeof(e) == 'undefined' && view == false){
18309                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18310                     }
18311                     
18312                     rm = true;
18313                     return;
18314                 }
18315             });
18316             
18317             if(rm){
18318                 return;
18319             }
18320             
18321             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18322                 this.tickItems.push(r.data);
18323             }
18324             
18325             if(typeof(e) == 'undefined' && view == false){
18326                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18327             }
18328                     
18329             return;
18330         }
18331         
18332         if(r){
18333             this.onSelect(r, index);
18334         }
18335         if(doFocus !== false && !this.blockFocus){
18336             this.inputEl().focus();
18337         }
18338     },
18339
18340     // private
18341     restrictHeight : function(){
18342         //this.innerList.dom.style.height = '';
18343         //var inner = this.innerList.dom;
18344         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18345         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18346         //this.list.beginUpdate();
18347         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18348         this.list.alignTo(this.inputEl(), this.listAlign);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         //this.list.endUpdate();
18351     },
18352
18353     // private
18354     onEmptyResults : function(){
18355         
18356         if(this.tickable && this.editable){
18357             this.hasFocus = false;
18358             this.restrictHeight();
18359             return;
18360         }
18361         
18362         this.collapse();
18363     },
18364
18365     /**
18366      * Returns true if the dropdown list is expanded, else false.
18367      */
18368     isExpanded : function(){
18369         return this.list.isVisible();
18370     },
18371
18372     /**
18373      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18374      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18375      * @param {String} value The data value of the item to select
18376      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18377      * selected item if it is not currently in view (defaults to true)
18378      * @return {Boolean} True if the value matched an item in the list, else false
18379      */
18380     selectByValue : function(v, scrollIntoView){
18381         if(v !== undefined && v !== null){
18382             var r = this.findRecord(this.valueField || this.displayField, v);
18383             if(r){
18384                 this.select(this.store.indexOf(r), scrollIntoView);
18385                 return true;
18386             }
18387         }
18388         return false;
18389     },
18390
18391     /**
18392      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18393      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18394      * @param {Number} index The zero-based index of the list item to select
18395      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18396      * selected item if it is not currently in view (defaults to true)
18397      */
18398     select : function(index, scrollIntoView){
18399         this.selectedIndex = index;
18400         this.view.select(index);
18401         if(scrollIntoView !== false){
18402             var el = this.view.getNode(index);
18403             /*
18404              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18405              */
18406             if(el){
18407                 this.list.scrollChildIntoView(el, false);
18408             }
18409         }
18410     },
18411
18412     // private
18413     selectNext : function(){
18414         var ct = this.store.getCount();
18415         if(ct > 0){
18416             if(this.selectedIndex == -1){
18417                 this.select(0);
18418             }else if(this.selectedIndex < ct-1){
18419                 this.select(this.selectedIndex+1);
18420             }
18421         }
18422     },
18423
18424     // private
18425     selectPrev : function(){
18426         var ct = this.store.getCount();
18427         if(ct > 0){
18428             if(this.selectedIndex == -1){
18429                 this.select(0);
18430             }else if(this.selectedIndex != 0){
18431                 this.select(this.selectedIndex-1);
18432             }
18433         }
18434     },
18435
18436     // private
18437     onKeyUp : function(e){
18438         if(this.editable !== false && !e.isSpecialKey()){
18439             this.lastKey = e.getKey();
18440             this.dqTask.delay(this.queryDelay);
18441         }
18442     },
18443
18444     // private
18445     validateBlur : function(){
18446         return !this.list || !this.list.isVisible();   
18447     },
18448
18449     // private
18450     initQuery : function(){
18451         
18452         var v = this.getRawValue();
18453         
18454         if(this.tickable && this.editable){
18455             v = this.tickableInputEl().getValue();
18456         }
18457         
18458         this.doQuery(v);
18459     },
18460
18461     // private
18462     doForce : function(){
18463         if(this.inputEl().dom.value.length > 0){
18464             this.inputEl().dom.value =
18465                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18466              
18467         }
18468     },
18469
18470     /**
18471      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18472      * query allowing the query action to be canceled if needed.
18473      * @param {String} query The SQL query to execute
18474      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18475      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18476      * saved in the current store (defaults to false)
18477      */
18478     doQuery : function(q, forceAll){
18479         
18480         if(q === undefined || q === null){
18481             q = '';
18482         }
18483         var qe = {
18484             query: q,
18485             forceAll: forceAll,
18486             combo: this,
18487             cancel:false
18488         };
18489         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18490             return false;
18491         }
18492         q = qe.query;
18493         
18494         forceAll = qe.forceAll;
18495         if(forceAll === true || (q.length >= this.minChars)){
18496             
18497             this.hasQuery = true;
18498             
18499             if(this.lastQuery != q || this.alwaysQuery){
18500                 this.lastQuery = q;
18501                 if(this.mode == 'local'){
18502                     this.selectedIndex = -1;
18503                     if(forceAll){
18504                         this.store.clearFilter();
18505                     }else{
18506                         
18507                         if(this.specialFilter){
18508                             this.fireEvent('specialfilter', this);
18509                             this.onLoad();
18510                             return;
18511                         }
18512                         
18513                         this.store.filter(this.displayField, q);
18514                     }
18515                     
18516                     this.store.fireEvent("datachanged", this.store);
18517                     
18518                     this.onLoad();
18519                     
18520                     
18521                 }else{
18522                     
18523                     this.store.baseParams[this.queryParam] = q;
18524                     
18525                     var options = {params : this.getParams(q)};
18526                     
18527                     if(this.loadNext){
18528                         options.add = true;
18529                         options.params.start = this.page * this.pageSize;
18530                     }
18531                     
18532                     this.store.load(options);
18533                     
18534                     /*
18535                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18536                      *  we should expand the list on onLoad
18537                      *  so command out it
18538                      */
18539 //                    this.expand();
18540                 }
18541             }else{
18542                 this.selectedIndex = -1;
18543                 this.onLoad();   
18544             }
18545         }
18546         
18547         this.loadNext = false;
18548     },
18549     
18550     // private
18551     getParams : function(q){
18552         var p = {};
18553         //p[this.queryParam] = q;
18554         
18555         if(this.pageSize){
18556             p.start = 0;
18557             p.limit = this.pageSize;
18558         }
18559         return p;
18560     },
18561
18562     /**
18563      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18564      */
18565     collapse : function(){
18566         if(!this.isExpanded()){
18567             return;
18568         }
18569         
18570         this.list.hide();
18571         
18572         this.hasFocus = false;
18573         
18574         if(this.tickable){
18575             this.okBtn.hide();
18576             this.cancelBtn.hide();
18577             this.trigger.show();
18578             
18579             if(this.editable){
18580                 this.tickableInputEl().dom.value = '';
18581                 this.tickableInputEl().blur();
18582             }
18583             
18584         }
18585         
18586         Roo.get(document).un('mousedown', this.collapseIf, this);
18587         Roo.get(document).un('mousewheel', this.collapseIf, this);
18588         if (!this.editable) {
18589             Roo.get(document).un('keydown', this.listKeyPress, this);
18590         }
18591         this.fireEvent('collapse', this);
18592         
18593         this.validate();
18594     },
18595
18596     // private
18597     collapseIf : function(e){
18598         var in_combo  = e.within(this.el);
18599         var in_list =  e.within(this.list);
18600         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18601         
18602         if (in_combo || in_list || is_list) {
18603             //e.stopPropagation();
18604             return;
18605         }
18606         
18607         if(this.tickable){
18608             this.onTickableFooterButtonClick(e, false, false);
18609         }
18610
18611         this.collapse();
18612         
18613     },
18614
18615     /**
18616      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18617      */
18618     expand : function(){
18619        
18620         if(this.isExpanded() || !this.hasFocus){
18621             return;
18622         }
18623         
18624         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18625         this.list.setWidth(lw);
18626         
18627         Roo.log('expand');
18628         
18629         this.list.show();
18630         
18631         this.restrictHeight();
18632         
18633         if(this.tickable){
18634             
18635             this.tickItems = Roo.apply([], this.item);
18636             
18637             this.okBtn.show();
18638             this.cancelBtn.show();
18639             this.trigger.hide();
18640             
18641             if(this.editable){
18642                 this.tickableInputEl().focus();
18643             }
18644             
18645         }
18646         
18647         Roo.get(document).on('mousedown', this.collapseIf, this);
18648         Roo.get(document).on('mousewheel', this.collapseIf, this);
18649         if (!this.editable) {
18650             Roo.get(document).on('keydown', this.listKeyPress, this);
18651         }
18652         
18653         this.fireEvent('expand', this);
18654     },
18655
18656     // private
18657     // Implements the default empty TriggerField.onTriggerClick function
18658     onTriggerClick : function(e)
18659     {
18660         Roo.log('trigger click');
18661         
18662         if(this.disabled || !this.triggerList){
18663             return;
18664         }
18665         
18666         this.page = 0;
18667         this.loadNext = false;
18668         
18669         if(this.isExpanded()){
18670             this.collapse();
18671             if (!this.blockFocus) {
18672                 this.inputEl().focus();
18673             }
18674             
18675         }else {
18676             this.hasFocus = true;
18677             if(this.triggerAction == 'all') {
18678                 this.doQuery(this.allQuery, true);
18679             } else {
18680                 this.doQuery(this.getRawValue());
18681             }
18682             if (!this.blockFocus) {
18683                 this.inputEl().focus();
18684             }
18685         }
18686     },
18687     
18688     onTickableTriggerClick : function(e)
18689     {
18690         if(this.disabled){
18691             return;
18692         }
18693         
18694         this.page = 0;
18695         this.loadNext = false;
18696         this.hasFocus = true;
18697         
18698         if(this.triggerAction == 'all') {
18699             this.doQuery(this.allQuery, true);
18700         } else {
18701             this.doQuery(this.getRawValue());
18702         }
18703     },
18704     
18705     onSearchFieldClick : function(e)
18706     {
18707         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18708             this.onTickableFooterButtonClick(e, false, false);
18709             return;
18710         }
18711         
18712         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18713             return;
18714         }
18715         
18716         this.page = 0;
18717         this.loadNext = false;
18718         this.hasFocus = true;
18719         
18720         if(this.triggerAction == 'all') {
18721             this.doQuery(this.allQuery, true);
18722         } else {
18723             this.doQuery(this.getRawValue());
18724         }
18725     },
18726     
18727     listKeyPress : function(e)
18728     {
18729         //Roo.log('listkeypress');
18730         // scroll to first matching element based on key pres..
18731         if (e.isSpecialKey()) {
18732             return false;
18733         }
18734         var k = String.fromCharCode(e.getKey()).toUpperCase();
18735         //Roo.log(k);
18736         var match  = false;
18737         var csel = this.view.getSelectedNodes();
18738         var cselitem = false;
18739         if (csel.length) {
18740             var ix = this.view.indexOf(csel[0]);
18741             cselitem  = this.store.getAt(ix);
18742             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18743                 cselitem = false;
18744             }
18745             
18746         }
18747         
18748         this.store.each(function(v) { 
18749             if (cselitem) {
18750                 // start at existing selection.
18751                 if (cselitem.id == v.id) {
18752                     cselitem = false;
18753                 }
18754                 return true;
18755             }
18756                 
18757             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18758                 match = this.store.indexOf(v);
18759                 return false;
18760             }
18761             return true;
18762         }, this);
18763         
18764         if (match === false) {
18765             return true; // no more action?
18766         }
18767         // scroll to?
18768         this.view.select(match);
18769         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18770         sn.scrollIntoView(sn.dom.parentNode, false);
18771     },
18772     
18773     onViewScroll : function(e, t){
18774         
18775         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){
18776             return;
18777         }
18778         
18779         this.hasQuery = true;
18780         
18781         this.loading = this.list.select('.loading', true).first();
18782         
18783         if(this.loading === null){
18784             this.list.createChild({
18785                 tag: 'div',
18786                 cls: 'loading roo-select2-more-results roo-select2-active',
18787                 html: 'Loading more results...'
18788             });
18789             
18790             this.loading = this.list.select('.loading', true).first();
18791             
18792             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18793             
18794             this.loading.hide();
18795         }
18796         
18797         this.loading.show();
18798         
18799         var _combo = this;
18800         
18801         this.page++;
18802         this.loadNext = true;
18803         
18804         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18805         
18806         return;
18807     },
18808     
18809     addItem : function(o)
18810     {   
18811         var dv = ''; // display value
18812         
18813         if (this.displayField) {
18814             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18815         } else {
18816             // this is an error condition!!!
18817             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18818         }
18819         
18820         if(!dv.length){
18821             return;
18822         }
18823         
18824         var choice = this.choices.createChild({
18825             tag: 'li',
18826             cls: 'roo-select2-search-choice',
18827             cn: [
18828                 {
18829                     tag: 'div',
18830                     html: dv
18831                 },
18832                 {
18833                     tag: 'a',
18834                     href: '#',
18835                     cls: 'roo-select2-search-choice-close fa fa-times',
18836                     tabindex: '-1'
18837                 }
18838             ]
18839             
18840         }, this.searchField);
18841         
18842         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18843         
18844         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18845         
18846         this.item.push(o);
18847         
18848         this.lastData = o;
18849         
18850         this.syncValue();
18851         
18852         this.inputEl().dom.value = '';
18853         
18854         this.validate();
18855     },
18856     
18857     onRemoveItem : function(e, _self, o)
18858     {
18859         e.preventDefault();
18860         
18861         this.lastItem = Roo.apply([], this.item);
18862         
18863         var index = this.item.indexOf(o.data) * 1;
18864         
18865         if( index < 0){
18866             Roo.log('not this item?!');
18867             return;
18868         }
18869         
18870         this.item.splice(index, 1);
18871         o.item.remove();
18872         
18873         this.syncValue();
18874         
18875         this.fireEvent('remove', this, e);
18876         
18877         this.validate();
18878         
18879     },
18880     
18881     syncValue : function()
18882     {
18883         if(!this.item.length){
18884             this.clearValue();
18885             return;
18886         }
18887             
18888         var value = [];
18889         var _this = this;
18890         Roo.each(this.item, function(i){
18891             if(_this.valueField){
18892                 value.push(i[_this.valueField]);
18893                 return;
18894             }
18895
18896             value.push(i);
18897         });
18898
18899         this.value = value.join(',');
18900
18901         if(this.hiddenField){
18902             this.hiddenField.dom.value = this.value;
18903         }
18904         
18905         this.store.fireEvent("datachanged", this.store);
18906         
18907         this.validate();
18908     },
18909     
18910     clearItem : function()
18911     {
18912         if(!this.multiple){
18913             return;
18914         }
18915         
18916         this.item = [];
18917         
18918         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18919            c.remove();
18920         });
18921         
18922         this.syncValue();
18923         
18924         this.validate();
18925         
18926         if(this.tickable && !Roo.isTouch){
18927             this.view.refresh();
18928         }
18929     },
18930     
18931     inputEl: function ()
18932     {
18933         if(Roo.isIOS && this.useNativeIOS){
18934             return this.el.select('select.roo-ios-select', true).first();
18935         }
18936         
18937         if(Roo.isTouch && this.mobileTouchView){
18938             return this.el.select('input.form-control',true).first();
18939         }
18940         
18941         if(this.tickable){
18942             return this.searchField;
18943         }
18944         
18945         return this.el.select('input.form-control',true).first();
18946     },
18947     
18948     onTickableFooterButtonClick : function(e, btn, el)
18949     {
18950         e.preventDefault();
18951         
18952         this.lastItem = Roo.apply([], this.item);
18953         
18954         if(btn && btn.name == 'cancel'){
18955             this.tickItems = Roo.apply([], this.item);
18956             this.collapse();
18957             return;
18958         }
18959         
18960         this.clearItem();
18961         
18962         var _this = this;
18963         
18964         Roo.each(this.tickItems, function(o){
18965             _this.addItem(o);
18966         });
18967         
18968         this.collapse();
18969         
18970     },
18971     
18972     validate : function()
18973     {
18974         if(this.getVisibilityEl().hasClass('hidden')){
18975             return true;
18976         }
18977         
18978         var v = this.getRawValue();
18979         
18980         if(this.multiple){
18981             v = this.getValue();
18982         }
18983         
18984         if(this.disabled || this.allowBlank || v.length){
18985             this.markValid();
18986             return true;
18987         }
18988         
18989         this.markInvalid();
18990         return false;
18991     },
18992     
18993     tickableInputEl : function()
18994     {
18995         if(!this.tickable || !this.editable){
18996             return this.inputEl();
18997         }
18998         
18999         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19000     },
19001     
19002     
19003     getAutoCreateTouchView : function()
19004     {
19005         var id = Roo.id();
19006         
19007         var cfg = {
19008             cls: 'form-group' //input-group
19009         };
19010         
19011         var input =  {
19012             tag: 'input',
19013             id : id,
19014             type : this.inputType,
19015             cls : 'form-control x-combo-noedit',
19016             autocomplete: 'new-password',
19017             placeholder : this.placeholder || '',
19018             readonly : true
19019         };
19020         
19021         if (this.name) {
19022             input.name = this.name;
19023         }
19024         
19025         if (this.size) {
19026             input.cls += ' input-' + this.size;
19027         }
19028         
19029         if (this.disabled) {
19030             input.disabled = true;
19031         }
19032         
19033         var inputblock = {
19034             cls : 'roo-combobox-wrap',
19035             cn : [
19036                 input
19037             ]
19038         };
19039         
19040         if(this.before){
19041             inputblock.cls += ' input-group';
19042             
19043             inputblock.cn.unshift({
19044                 tag :'span',
19045                 cls : 'input-group-addon input-group-prepend input-group-text',
19046                 html : this.before
19047             });
19048         }
19049         
19050         if(this.removable && !this.multiple){
19051             inputblock.cls += ' roo-removable';
19052             
19053             inputblock.cn.push({
19054                 tag: 'button',
19055                 html : 'x',
19056                 cls : 'roo-combo-removable-btn close'
19057             });
19058         }
19059
19060         if(this.hasFeedback && !this.allowBlank){
19061             
19062             inputblock.cls += ' has-feedback';
19063             
19064             inputblock.cn.push({
19065                 tag: 'span',
19066                 cls: 'glyphicon form-control-feedback'
19067             });
19068             
19069         }
19070         
19071         if (this.after) {
19072             
19073             inputblock.cls += (this.before) ? '' : ' input-group';
19074             
19075             inputblock.cn.push({
19076                 tag :'span',
19077                 cls : 'input-group-addon input-group-append input-group-text',
19078                 html : this.after
19079             });
19080         }
19081
19082         
19083         var ibwrap = inputblock;
19084         
19085         if(this.multiple){
19086             ibwrap = {
19087                 tag: 'ul',
19088                 cls: 'roo-select2-choices',
19089                 cn:[
19090                     {
19091                         tag: 'li',
19092                         cls: 'roo-select2-search-field',
19093                         cn: [
19094
19095                             inputblock
19096                         ]
19097                     }
19098                 ]
19099             };
19100         
19101             
19102         }
19103         
19104         var combobox = {
19105             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19106             cn: [
19107                 {
19108                     tag: 'input',
19109                     type : 'hidden',
19110                     cls: 'form-hidden-field'
19111                 },
19112                 ibwrap
19113             ]
19114         };
19115         
19116         if(!this.multiple && this.showToggleBtn){
19117             
19118             var caret = {
19119                 cls: 'caret'
19120             };
19121             
19122             if (this.caret != false) {
19123                 caret = {
19124                      tag: 'i',
19125                      cls: 'fa fa-' + this.caret
19126                 };
19127                 
19128             }
19129             
19130             combobox.cn.push({
19131                 tag :'span',
19132                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19133                 cn : [
19134                     Roo.bootstrap.version == 3 ? caret : '',
19135                     {
19136                         tag: 'span',
19137                         cls: 'combobox-clear',
19138                         cn  : [
19139                             {
19140                                 tag : 'i',
19141                                 cls: 'icon-remove'
19142                             }
19143                         ]
19144                     }
19145                 ]
19146
19147             })
19148         }
19149         
19150         if(this.multiple){
19151             combobox.cls += ' roo-select2-container-multi';
19152         }
19153         
19154         var required =  this.allowBlank ?  {
19155                     tag : 'i',
19156                     style: 'display: none'
19157                 } : {
19158                    tag : 'i',
19159                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19160                    tooltip : 'This field is required'
19161                 };
19162         
19163         var align = this.labelAlign || this.parentLabelAlign();
19164         
19165         if (align ==='left' && this.fieldLabel.length) {
19166
19167             cfg.cn = [
19168                 required,
19169                 {
19170                     tag: 'label',
19171                     cls : 'control-label col-form-label',
19172                     html : this.fieldLabel
19173
19174                 },
19175                 {
19176                     cls : 'roo-combobox-wrap ', 
19177                     cn: [
19178                         combobox
19179                     ]
19180                 }
19181             ];
19182             
19183             var labelCfg = cfg.cn[1];
19184             var contentCfg = cfg.cn[2];
19185             
19186
19187             if(this.indicatorpos == 'right'){
19188                 cfg.cn = [
19189                     {
19190                         tag: 'label',
19191                         'for' :  id,
19192                         cls : 'control-label col-form-label',
19193                         cn : [
19194                             {
19195                                 tag : 'span',
19196                                 html : this.fieldLabel
19197                             },
19198                             required
19199                         ]
19200                     },
19201                     {
19202                         cls : "roo-combobox-wrap ",
19203                         cn: [
19204                             combobox
19205                         ]
19206                     }
19207
19208                 ];
19209                 
19210                 labelCfg = cfg.cn[0];
19211                 contentCfg = cfg.cn[1];
19212             }
19213             
19214            
19215             
19216             if(this.labelWidth > 12){
19217                 labelCfg.style = "width: " + this.labelWidth + 'px';
19218             }
19219            
19220             if(this.labelWidth < 13 && this.labelmd == 0){
19221                 this.labelmd = this.labelWidth;
19222             }
19223             
19224             if(this.labellg > 0){
19225                 labelCfg.cls += ' col-lg-' + this.labellg;
19226                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19227             }
19228             
19229             if(this.labelmd > 0){
19230                 labelCfg.cls += ' col-md-' + this.labelmd;
19231                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19232             }
19233             
19234             if(this.labelsm > 0){
19235                 labelCfg.cls += ' col-sm-' + this.labelsm;
19236                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19237             }
19238             
19239             if(this.labelxs > 0){
19240                 labelCfg.cls += ' col-xs-' + this.labelxs;
19241                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19242             }
19243                 
19244                 
19245         } else if ( this.fieldLabel.length) {
19246             cfg.cn = [
19247                required,
19248                 {
19249                     tag: 'label',
19250                     cls : 'control-label',
19251                     html : this.fieldLabel
19252
19253                 },
19254                 {
19255                     cls : '', 
19256                     cn: [
19257                         combobox
19258                     ]
19259                 }
19260             ];
19261             
19262             if(this.indicatorpos == 'right'){
19263                 cfg.cn = [
19264                     {
19265                         tag: 'label',
19266                         cls : 'control-label',
19267                         html : this.fieldLabel,
19268                         cn : [
19269                             required
19270                         ]
19271                     },
19272                     {
19273                         cls : '', 
19274                         cn: [
19275                             combobox
19276                         ]
19277                     }
19278                 ];
19279             }
19280         } else {
19281             cfg.cn = combobox;    
19282         }
19283         
19284         
19285         var settings = this;
19286         
19287         ['xs','sm','md','lg'].map(function(size){
19288             if (settings[size]) {
19289                 cfg.cls += ' col-' + size + '-' + settings[size];
19290             }
19291         });
19292         
19293         return cfg;
19294     },
19295     
19296     initTouchView : function()
19297     {
19298         this.renderTouchView();
19299         
19300         this.touchViewEl.on('scroll', function(){
19301             this.el.dom.scrollTop = 0;
19302         }, this);
19303         
19304         this.originalValue = this.getValue();
19305         
19306         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19307         
19308         this.inputEl().on("click", this.showTouchView, this);
19309         if (this.triggerEl) {
19310             this.triggerEl.on("click", this.showTouchView, this);
19311         }
19312         
19313         
19314         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19315         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19316         
19317         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19318         
19319         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19320         this.store.on('load', this.onTouchViewLoad, this);
19321         this.store.on('loadexception', this.onTouchViewLoadException, this);
19322         
19323         if(this.hiddenName){
19324             
19325             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19326             
19327             this.hiddenField.dom.value =
19328                 this.hiddenValue !== undefined ? this.hiddenValue :
19329                 this.value !== undefined ? this.value : '';
19330         
19331             this.el.dom.removeAttribute('name');
19332             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19333         }
19334         
19335         if(this.multiple){
19336             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19337             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19338         }
19339         
19340         if(this.removable && !this.multiple){
19341             var close = this.closeTriggerEl();
19342             if(close){
19343                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19344                 close.on('click', this.removeBtnClick, this, close);
19345             }
19346         }
19347         /*
19348          * fix the bug in Safari iOS8
19349          */
19350         this.inputEl().on("focus", function(e){
19351             document.activeElement.blur();
19352         }, this);
19353         
19354         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19355         
19356         return;
19357         
19358         
19359     },
19360     
19361     renderTouchView : function()
19362     {
19363         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19364         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365         
19366         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19367         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368         
19369         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19370         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371         this.touchViewBodyEl.setStyle('overflow', 'auto');
19372         
19373         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19374         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         
19376         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19377         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378         
19379     },
19380     
19381     showTouchView : function()
19382     {
19383         if(this.disabled){
19384             return;
19385         }
19386         
19387         this.touchViewHeaderEl.hide();
19388
19389         if(this.modalTitle.length){
19390             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19391             this.touchViewHeaderEl.show();
19392         }
19393
19394         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19395         this.touchViewEl.show();
19396
19397         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19398         
19399         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19400         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19401
19402         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19403
19404         if(this.modalTitle.length){
19405             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19406         }
19407         
19408         this.touchViewBodyEl.setHeight(bodyHeight);
19409
19410         if(this.animate){
19411             var _this = this;
19412             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19413         }else{
19414             this.touchViewEl.addClass(['in','show']);
19415         }
19416         
19417         if(this._touchViewMask){
19418             Roo.get(document.body).addClass("x-body-masked");
19419             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19420             this._touchViewMask.setStyle('z-index', 10000);
19421             this._touchViewMask.addClass('show');
19422         }
19423         
19424         this.doTouchViewQuery();
19425         
19426     },
19427     
19428     hideTouchView : function()
19429     {
19430         this.touchViewEl.removeClass(['in','show']);
19431
19432         if(this.animate){
19433             var _this = this;
19434             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19435         }else{
19436             this.touchViewEl.setStyle('display', 'none');
19437         }
19438         
19439         if(this._touchViewMask){
19440             this._touchViewMask.removeClass('show');
19441             Roo.get(document.body).removeClass("x-body-masked");
19442         }
19443     },
19444     
19445     setTouchViewValue : function()
19446     {
19447         if(this.multiple){
19448             this.clearItem();
19449         
19450             var _this = this;
19451
19452             Roo.each(this.tickItems, function(o){
19453                 this.addItem(o);
19454             }, this);
19455         }
19456         
19457         this.hideTouchView();
19458     },
19459     
19460     doTouchViewQuery : function()
19461     {
19462         var qe = {
19463             query: '',
19464             forceAll: true,
19465             combo: this,
19466             cancel:false
19467         };
19468         
19469         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19470             return false;
19471         }
19472         
19473         if(!this.alwaysQuery || this.mode == 'local'){
19474             this.onTouchViewLoad();
19475             return;
19476         }
19477         
19478         this.store.load();
19479     },
19480     
19481     onTouchViewBeforeLoad : function(combo,opts)
19482     {
19483         return;
19484     },
19485
19486     // private
19487     onTouchViewLoad : function()
19488     {
19489         if(this.store.getCount() < 1){
19490             this.onTouchViewEmptyResults();
19491             return;
19492         }
19493         
19494         this.clearTouchView();
19495         
19496         var rawValue = this.getRawValue();
19497         
19498         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19499         
19500         this.tickItems = [];
19501         
19502         this.store.data.each(function(d, rowIndex){
19503             var row = this.touchViewListGroup.createChild(template);
19504             
19505             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19506                 row.addClass(d.data.cls);
19507             }
19508             
19509             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19510                 var cfg = {
19511                     data : d.data,
19512                     html : d.data[this.displayField]
19513                 };
19514                 
19515                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19516                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19517                 }
19518             }
19519             row.removeClass('selected');
19520             if(!this.multiple && this.valueField &&
19521                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19522             {
19523                 // radio buttons..
19524                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19525                 row.addClass('selected');
19526             }
19527             
19528             if(this.multiple && this.valueField &&
19529                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19530             {
19531                 
19532                 // checkboxes...
19533                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19534                 this.tickItems.push(d.data);
19535             }
19536             
19537             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19538             
19539         }, this);
19540         
19541         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19542         
19543         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19544
19545         if(this.modalTitle.length){
19546             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19547         }
19548
19549         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19550         
19551         if(this.mobile_restrict_height && listHeight < bodyHeight){
19552             this.touchViewBodyEl.setHeight(listHeight);
19553         }
19554         
19555         var _this = this;
19556         
19557         if(firstChecked && listHeight > bodyHeight){
19558             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19559         }
19560         
19561     },
19562     
19563     onTouchViewLoadException : function()
19564     {
19565         this.hideTouchView();
19566     },
19567     
19568     onTouchViewEmptyResults : function()
19569     {
19570         this.clearTouchView();
19571         
19572         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19573         
19574         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19575         
19576     },
19577     
19578     clearTouchView : function()
19579     {
19580         this.touchViewListGroup.dom.innerHTML = '';
19581     },
19582     
19583     onTouchViewClick : function(e, el, o)
19584     {
19585         e.preventDefault();
19586         
19587         var row = o.row;
19588         var rowIndex = o.rowIndex;
19589         
19590         var r = this.store.getAt(rowIndex);
19591         
19592         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19593             
19594             if(!this.multiple){
19595                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19596                     c.dom.removeAttribute('checked');
19597                 }, this);
19598
19599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19600
19601                 this.setFromData(r.data);
19602
19603                 var close = this.closeTriggerEl();
19604
19605                 if(close){
19606                     close.show();
19607                 }
19608
19609                 this.hideTouchView();
19610
19611                 this.fireEvent('select', this, r, rowIndex);
19612
19613                 return;
19614             }
19615
19616             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19617                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19618                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19619                 return;
19620             }
19621
19622             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19623             this.addItem(r.data);
19624             this.tickItems.push(r.data);
19625         }
19626     },
19627     
19628     getAutoCreateNativeIOS : function()
19629     {
19630         var cfg = {
19631             cls: 'form-group' //input-group,
19632         };
19633         
19634         var combobox =  {
19635             tag: 'select',
19636             cls : 'roo-ios-select'
19637         };
19638         
19639         if (this.name) {
19640             combobox.name = this.name;
19641         }
19642         
19643         if (this.disabled) {
19644             combobox.disabled = true;
19645         }
19646         
19647         var settings = this;
19648         
19649         ['xs','sm','md','lg'].map(function(size){
19650             if (settings[size]) {
19651                 cfg.cls += ' col-' + size + '-' + settings[size];
19652             }
19653         });
19654         
19655         cfg.cn = combobox;
19656         
19657         return cfg;
19658         
19659     },
19660     
19661     initIOSView : function()
19662     {
19663         this.store.on('load', this.onIOSViewLoad, this);
19664         
19665         return;
19666     },
19667     
19668     onIOSViewLoad : function()
19669     {
19670         if(this.store.getCount() < 1){
19671             return;
19672         }
19673         
19674         this.clearIOSView();
19675         
19676         if(this.allowBlank) {
19677             
19678             var default_text = '-- SELECT --';
19679             
19680             if(this.placeholder.length){
19681                 default_text = this.placeholder;
19682             }
19683             
19684             if(this.emptyTitle.length){
19685                 default_text += ' - ' + this.emptyTitle + ' -';
19686             }
19687             
19688             var opt = this.inputEl().createChild({
19689                 tag: 'option',
19690                 value : 0,
19691                 html : default_text
19692             });
19693             
19694             var o = {};
19695             o[this.valueField] = 0;
19696             o[this.displayField] = default_text;
19697             
19698             this.ios_options.push({
19699                 data : o,
19700                 el : opt
19701             });
19702             
19703         }
19704         
19705         this.store.data.each(function(d, rowIndex){
19706             
19707             var html = '';
19708             
19709             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19710                 html = d.data[this.displayField];
19711             }
19712             
19713             var value = '';
19714             
19715             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19716                 value = d.data[this.valueField];
19717             }
19718             
19719             var option = {
19720                 tag: 'option',
19721                 value : value,
19722                 html : html
19723             };
19724             
19725             if(this.value == d.data[this.valueField]){
19726                 option['selected'] = true;
19727             }
19728             
19729             var opt = this.inputEl().createChild(option);
19730             
19731             this.ios_options.push({
19732                 data : d.data,
19733                 el : opt
19734             });
19735             
19736         }, this);
19737         
19738         this.inputEl().on('change', function(){
19739            this.fireEvent('select', this);
19740         }, this);
19741         
19742     },
19743     
19744     clearIOSView: function()
19745     {
19746         this.inputEl().dom.innerHTML = '';
19747         
19748         this.ios_options = [];
19749     },
19750     
19751     setIOSValue: function(v)
19752     {
19753         this.value = v;
19754         
19755         if(!this.ios_options){
19756             return;
19757         }
19758         
19759         Roo.each(this.ios_options, function(opts){
19760            
19761            opts.el.dom.removeAttribute('selected');
19762            
19763            if(opts.data[this.valueField] != v){
19764                return;
19765            }
19766            
19767            opts.el.dom.setAttribute('selected', true);
19768            
19769         }, this);
19770     }
19771
19772     /** 
19773     * @cfg {Boolean} grow 
19774     * @hide 
19775     */
19776     /** 
19777     * @cfg {Number} growMin 
19778     * @hide 
19779     */
19780     /** 
19781     * @cfg {Number} growMax 
19782     * @hide 
19783     */
19784     /**
19785      * @hide
19786      * @method autoSize
19787      */
19788 });
19789
19790 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19791     
19792     header : {
19793         tag: 'div',
19794         cls: 'modal-header',
19795         cn: [
19796             {
19797                 tag: 'h4',
19798                 cls: 'modal-title'
19799             }
19800         ]
19801     },
19802     
19803     body : {
19804         tag: 'div',
19805         cls: 'modal-body',
19806         cn: [
19807             {
19808                 tag: 'ul',
19809                 cls: 'list-group'
19810             }
19811         ]
19812     },
19813     
19814     listItemRadio : {
19815         tag: 'li',
19816         cls: 'list-group-item',
19817         cn: [
19818             {
19819                 tag: 'span',
19820                 cls: 'roo-combobox-list-group-item-value'
19821             },
19822             {
19823                 tag: 'div',
19824                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19825                 cn: [
19826                     {
19827                         tag: 'input',
19828                         type: 'radio'
19829                     },
19830                     {
19831                         tag: 'label'
19832                     }
19833                 ]
19834             }
19835         ]
19836     },
19837     
19838     listItemCheckbox : {
19839         tag: 'li',
19840         cls: 'list-group-item',
19841         cn: [
19842             {
19843                 tag: 'span',
19844                 cls: 'roo-combobox-list-group-item-value'
19845             },
19846             {
19847                 tag: 'div',
19848                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19849                 cn: [
19850                     {
19851                         tag: 'input',
19852                         type: 'checkbox'
19853                     },
19854                     {
19855                         tag: 'label'
19856                     }
19857                 ]
19858             }
19859         ]
19860     },
19861     
19862     emptyResult : {
19863         tag: 'div',
19864         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19865     },
19866     
19867     footer : {
19868         tag: 'div',
19869         cls: 'modal-footer',
19870         cn: [
19871             {
19872                 tag: 'div',
19873                 cls: 'row',
19874                 cn: [
19875                     {
19876                         tag: 'div',
19877                         cls: 'col-xs-6 text-left',
19878                         cn: {
19879                             tag: 'button',
19880                             cls: 'btn btn-danger roo-touch-view-cancel',
19881                             html: 'Cancel'
19882                         }
19883                     },
19884                     {
19885                         tag: 'div',
19886                         cls: 'col-xs-6 text-right',
19887                         cn: {
19888                             tag: 'button',
19889                             cls: 'btn btn-success roo-touch-view-ok',
19890                             html: 'OK'
19891                         }
19892                     }
19893                 ]
19894             }
19895         ]
19896         
19897     }
19898 });
19899
19900 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19901     
19902     touchViewTemplate : {
19903         tag: 'div',
19904         cls: 'modal fade roo-combobox-touch-view',
19905         cn: [
19906             {
19907                 tag: 'div',
19908                 cls: 'modal-dialog',
19909                 style : 'position:fixed', // we have to fix position....
19910                 cn: [
19911                     {
19912                         tag: 'div',
19913                         cls: 'modal-content',
19914                         cn: [
19915                             Roo.bootstrap.form.ComboBox.header,
19916                             Roo.bootstrap.form.ComboBox.body,
19917                             Roo.bootstrap.form.ComboBox.footer
19918                         ]
19919                     }
19920                 ]
19921             }
19922         ]
19923     }
19924 });/*
19925  * Based on:
19926  * Ext JS Library 1.1.1
19927  * Copyright(c) 2006-2007, Ext JS, LLC.
19928  *
19929  * Originally Released Under LGPL - original licence link has changed is not relivant.
19930  *
19931  * Fork - LGPL
19932  * <script type="text/javascript">
19933  */
19934
19935 /**
19936  * @class Roo.View
19937  * @extends Roo.util.Observable
19938  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19939  * This class also supports single and multi selection modes. <br>
19940  * Create a data model bound view:
19941  <pre><code>
19942  var store = new Roo.data.Store(...);
19943
19944  var view = new Roo.View({
19945     el : "my-element",
19946     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19947  
19948     singleSelect: true,
19949     selectedClass: "ydataview-selected",
19950     store: store
19951  });
19952
19953  // listen for node click?
19954  view.on("click", function(vw, index, node, e){
19955  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19956  });
19957
19958  // load XML data
19959  dataModel.load("foobar.xml");
19960  </code></pre>
19961  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19962  * <br><br>
19963  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19964  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19965  * 
19966  * Note: old style constructor is still suported (container, template, config)
19967  * 
19968  * @constructor
19969  * Create a new View
19970  * @param {Object} config The config object
19971  * 
19972  */
19973 Roo.View = function(config, depreciated_tpl, depreciated_config){
19974     
19975     this.parent = false;
19976     
19977     if (typeof(depreciated_tpl) == 'undefined') {
19978         // new way.. - universal constructor.
19979         Roo.apply(this, config);
19980         this.el  = Roo.get(this.el);
19981     } else {
19982         // old format..
19983         this.el  = Roo.get(config);
19984         this.tpl = depreciated_tpl;
19985         Roo.apply(this, depreciated_config);
19986     }
19987     this.wrapEl  = this.el.wrap().wrap();
19988     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19989     
19990     
19991     if(typeof(this.tpl) == "string"){
19992         this.tpl = new Roo.Template(this.tpl);
19993     } else {
19994         // support xtype ctors..
19995         this.tpl = new Roo.factory(this.tpl, Roo);
19996     }
19997     
19998     
19999     this.tpl.compile();
20000     
20001     /** @private */
20002     this.addEvents({
20003         /**
20004          * @event beforeclick
20005          * Fires before a click is processed. Returns false to cancel the default action.
20006          * @param {Roo.View} this
20007          * @param {Number} index The index of the target node
20008          * @param {HTMLElement} node The target node
20009          * @param {Roo.EventObject} e The raw event object
20010          */
20011             "beforeclick" : true,
20012         /**
20013          * @event click
20014          * Fires when a template node is clicked.
20015          * @param {Roo.View} this
20016          * @param {Number} index The index of the target node
20017          * @param {HTMLElement} node The target node
20018          * @param {Roo.EventObject} e The raw event object
20019          */
20020             "click" : true,
20021         /**
20022          * @event dblclick
20023          * Fires when a template node is double clicked.
20024          * @param {Roo.View} this
20025          * @param {Number} index The index of the target node
20026          * @param {HTMLElement} node The target node
20027          * @param {Roo.EventObject} e The raw event object
20028          */
20029             "dblclick" : true,
20030         /**
20031          * @event contextmenu
20032          * Fires when a template node is right clicked.
20033          * @param {Roo.View} this
20034          * @param {Number} index The index of the target node
20035          * @param {HTMLElement} node The target node
20036          * @param {Roo.EventObject} e The raw event object
20037          */
20038             "contextmenu" : true,
20039         /**
20040          * @event selectionchange
20041          * Fires when the selected nodes change.
20042          * @param {Roo.View} this
20043          * @param {Array} selections Array of the selected nodes
20044          */
20045             "selectionchange" : true,
20046     
20047         /**
20048          * @event beforeselect
20049          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20050          * @param {Roo.View} this
20051          * @param {HTMLElement} node The node to be selected
20052          * @param {Array} selections Array of currently selected nodes
20053          */
20054             "beforeselect" : true,
20055         /**
20056          * @event preparedata
20057          * Fires on every row to render, to allow you to change the data.
20058          * @param {Roo.View} this
20059          * @param {Object} data to be rendered (change this)
20060          */
20061           "preparedata" : true
20062           
20063           
20064         });
20065
20066
20067
20068     this.el.on({
20069         "click": this.onClick,
20070         "dblclick": this.onDblClick,
20071         "contextmenu": this.onContextMenu,
20072         scope:this
20073     });
20074
20075     this.selections = [];
20076     this.nodes = [];
20077     this.cmp = new Roo.CompositeElementLite([]);
20078     if(this.store){
20079         this.store = Roo.factory(this.store, Roo.data);
20080         this.setStore(this.store, true);
20081     }
20082     
20083     if ( this.footer && this.footer.xtype) {
20084            
20085          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20086         
20087         this.footer.dataSource = this.store;
20088         this.footer.container = fctr;
20089         this.footer = Roo.factory(this.footer, Roo);
20090         fctr.insertFirst(this.el);
20091         
20092         // this is a bit insane - as the paging toolbar seems to detach the el..
20093 //        dom.parentNode.parentNode.parentNode
20094          // they get detached?
20095     }
20096     
20097     
20098     Roo.View.superclass.constructor.call(this);
20099     
20100     
20101 };
20102
20103 Roo.extend(Roo.View, Roo.util.Observable, {
20104     
20105      /**
20106      * @cfg {Roo.data.Store} store Data store to load data from.
20107      */
20108     store : false,
20109     
20110     /**
20111      * @cfg {String|Roo.Element} el The container element.
20112      */
20113     el : '',
20114     
20115     /**
20116      * @cfg {String|Roo.Template} tpl The template used by this View 
20117      */
20118     tpl : false,
20119     /**
20120      * @cfg {String} dataName the named area of the template to use as the data area
20121      *                          Works with domtemplates roo-name="name"
20122      */
20123     dataName: false,
20124     /**
20125      * @cfg {String} selectedClass The css class to add to selected nodes
20126      */
20127     selectedClass : "x-view-selected",
20128      /**
20129      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20130      */
20131     emptyText : "",
20132     
20133     /**
20134      * @cfg {String} text to display on mask (default Loading)
20135      */
20136     mask : false,
20137     /**
20138      * @cfg {Boolean} multiSelect Allow multiple selection
20139      */
20140     multiSelect : false,
20141     /**
20142      * @cfg {Boolean} singleSelect Allow single selection
20143      */
20144     singleSelect:  false,
20145     
20146     /**
20147      * @cfg {Boolean} toggleSelect - selecting 
20148      */
20149     toggleSelect : false,
20150     
20151     /**
20152      * @cfg {Boolean} tickable - selecting 
20153      */
20154     tickable : false,
20155     
20156     /**
20157      * Returns the element this view is bound to.
20158      * @return {Roo.Element}
20159      */
20160     getEl : function(){
20161         return this.wrapEl;
20162     },
20163     
20164     
20165
20166     /**
20167      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20168      */
20169     refresh : function(){
20170         //Roo.log('refresh');
20171         var t = this.tpl;
20172         
20173         // if we are using something like 'domtemplate', then
20174         // the what gets used is:
20175         // t.applySubtemplate(NAME, data, wrapping data..)
20176         // the outer template then get' applied with
20177         //     the store 'extra data'
20178         // and the body get's added to the
20179         //      roo-name="data" node?
20180         //      <span class='roo-tpl-{name}'></span> ?????
20181         
20182         
20183         
20184         this.clearSelections();
20185         this.el.update("");
20186         var html = [];
20187         var records = this.store.getRange();
20188         if(records.length < 1) {
20189             
20190             // is this valid??  = should it render a template??
20191             
20192             this.el.update(this.emptyText);
20193             return;
20194         }
20195         var el = this.el;
20196         if (this.dataName) {
20197             this.el.update(t.apply(this.store.meta)); //????
20198             el = this.el.child('.roo-tpl-' + this.dataName);
20199         }
20200         
20201         for(var i = 0, len = records.length; i < len; i++){
20202             var data = this.prepareData(records[i].data, i, records[i]);
20203             this.fireEvent("preparedata", this, data, i, records[i]);
20204             
20205             var d = Roo.apply({}, data);
20206             
20207             if(this.tickable){
20208                 Roo.apply(d, {'roo-id' : Roo.id()});
20209                 
20210                 var _this = this;
20211             
20212                 Roo.each(this.parent.item, function(item){
20213                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20214                         return;
20215                     }
20216                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20217                 });
20218             }
20219             
20220             html[html.length] = Roo.util.Format.trim(
20221                 this.dataName ?
20222                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20223                     t.apply(d)
20224             );
20225         }
20226         
20227         
20228         
20229         el.update(html.join(""));
20230         this.nodes = el.dom.childNodes;
20231         this.updateIndexes(0);
20232     },
20233     
20234
20235     /**
20236      * Function to override to reformat the data that is sent to
20237      * the template for each node.
20238      * DEPRICATED - use the preparedata event handler.
20239      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20240      * a JSON object for an UpdateManager bound view).
20241      */
20242     prepareData : function(data, index, record)
20243     {
20244         this.fireEvent("preparedata", this, data, index, record);
20245         return data;
20246     },
20247
20248     onUpdate : function(ds, record){
20249         // Roo.log('on update');   
20250         this.clearSelections();
20251         var index = this.store.indexOf(record);
20252         var n = this.nodes[index];
20253         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20254         n.parentNode.removeChild(n);
20255         this.updateIndexes(index, index);
20256     },
20257
20258     
20259     
20260 // --------- FIXME     
20261     onAdd : function(ds, records, index)
20262     {
20263         //Roo.log(['on Add', ds, records, index] );        
20264         this.clearSelections();
20265         if(this.nodes.length == 0){
20266             this.refresh();
20267             return;
20268         }
20269         var n = this.nodes[index];
20270         for(var i = 0, len = records.length; i < len; i++){
20271             var d = this.prepareData(records[i].data, i, records[i]);
20272             if(n){
20273                 this.tpl.insertBefore(n, d);
20274             }else{
20275                 
20276                 this.tpl.append(this.el, d);
20277             }
20278         }
20279         this.updateIndexes(index);
20280     },
20281
20282     onRemove : function(ds, record, index){
20283        // Roo.log('onRemove');
20284         this.clearSelections();
20285         var el = this.dataName  ?
20286             this.el.child('.roo-tpl-' + this.dataName) :
20287             this.el; 
20288         
20289         el.dom.removeChild(this.nodes[index]);
20290         this.updateIndexes(index);
20291     },
20292
20293     /**
20294      * Refresh an individual node.
20295      * @param {Number} index
20296      */
20297     refreshNode : function(index){
20298         this.onUpdate(this.store, this.store.getAt(index));
20299     },
20300
20301     updateIndexes : function(startIndex, endIndex){
20302         var ns = this.nodes;
20303         startIndex = startIndex || 0;
20304         endIndex = endIndex || ns.length - 1;
20305         for(var i = startIndex; i <= endIndex; i++){
20306             ns[i].nodeIndex = i;
20307         }
20308     },
20309
20310     /**
20311      * Changes the data store this view uses and refresh the view.
20312      * @param {Store} store
20313      */
20314     setStore : function(store, initial){
20315         if(!initial && this.store){
20316             this.store.un("datachanged", this.refresh);
20317             this.store.un("add", this.onAdd);
20318             this.store.un("remove", this.onRemove);
20319             this.store.un("update", this.onUpdate);
20320             this.store.un("clear", this.refresh);
20321             this.store.un("beforeload", this.onBeforeLoad);
20322             this.store.un("load", this.onLoad);
20323             this.store.un("loadexception", this.onLoad);
20324         }
20325         if(store){
20326           
20327             store.on("datachanged", this.refresh, this);
20328             store.on("add", this.onAdd, this);
20329             store.on("remove", this.onRemove, this);
20330             store.on("update", this.onUpdate, this);
20331             store.on("clear", this.refresh, this);
20332             store.on("beforeload", this.onBeforeLoad, this);
20333             store.on("load", this.onLoad, this);
20334             store.on("loadexception", this.onLoad, this);
20335         }
20336         
20337         if(store){
20338             this.refresh();
20339         }
20340     },
20341     /**
20342      * onbeforeLoad - masks the loading area.
20343      *
20344      */
20345     onBeforeLoad : function(store,opts)
20346     {
20347          //Roo.log('onBeforeLoad');   
20348         if (!opts.add) {
20349             this.el.update("");
20350         }
20351         this.el.mask(this.mask ? this.mask : "Loading" ); 
20352     },
20353     onLoad : function ()
20354     {
20355         this.el.unmask();
20356     },
20357     
20358
20359     /**
20360      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20361      * @param {HTMLElement} node
20362      * @return {HTMLElement} The template node
20363      */
20364     findItemFromChild : function(node){
20365         var el = this.dataName  ?
20366             this.el.child('.roo-tpl-' + this.dataName,true) :
20367             this.el.dom; 
20368         
20369         if(!node || node.parentNode == el){
20370                     return node;
20371             }
20372             var p = node.parentNode;
20373             while(p && p != el){
20374             if(p.parentNode == el){
20375                 return p;
20376             }
20377             p = p.parentNode;
20378         }
20379             return null;
20380     },
20381
20382     /** @ignore */
20383     onClick : function(e){
20384         var item = this.findItemFromChild(e.getTarget());
20385         if(item){
20386             var index = this.indexOf(item);
20387             if(this.onItemClick(item, index, e) !== false){
20388                 this.fireEvent("click", this, index, item, e);
20389             }
20390         }else{
20391             this.clearSelections();
20392         }
20393     },
20394
20395     /** @ignore */
20396     onContextMenu : function(e){
20397         var item = this.findItemFromChild(e.getTarget());
20398         if(item){
20399             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20400         }
20401     },
20402
20403     /** @ignore */
20404     onDblClick : function(e){
20405         var item = this.findItemFromChild(e.getTarget());
20406         if(item){
20407             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20408         }
20409     },
20410
20411     onItemClick : function(item, index, e)
20412     {
20413         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20414             return false;
20415         }
20416         if (this.toggleSelect) {
20417             var m = this.isSelected(item) ? 'unselect' : 'select';
20418             //Roo.log(m);
20419             var _t = this;
20420             _t[m](item, true, false);
20421             return true;
20422         }
20423         if(this.multiSelect || this.singleSelect){
20424             if(this.multiSelect && e.shiftKey && this.lastSelection){
20425                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20426             }else{
20427                 this.select(item, this.multiSelect && e.ctrlKey);
20428                 this.lastSelection = item;
20429             }
20430             
20431             if(!this.tickable){
20432                 e.preventDefault();
20433             }
20434             
20435         }
20436         return true;
20437     },
20438
20439     /**
20440      * Get the number of selected nodes.
20441      * @return {Number}
20442      */
20443     getSelectionCount : function(){
20444         return this.selections.length;
20445     },
20446
20447     /**
20448      * Get the currently selected nodes.
20449      * @return {Array} An array of HTMLElements
20450      */
20451     getSelectedNodes : function(){
20452         return this.selections;
20453     },
20454
20455     /**
20456      * Get the indexes of the selected nodes.
20457      * @return {Array}
20458      */
20459     getSelectedIndexes : function(){
20460         var indexes = [], s = this.selections;
20461         for(var i = 0, len = s.length; i < len; i++){
20462             indexes.push(s[i].nodeIndex);
20463         }
20464         return indexes;
20465     },
20466
20467     /**
20468      * Clear all selections
20469      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20470      */
20471     clearSelections : function(suppressEvent){
20472         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20473             this.cmp.elements = this.selections;
20474             this.cmp.removeClass(this.selectedClass);
20475             this.selections = [];
20476             if(!suppressEvent){
20477                 this.fireEvent("selectionchange", this, this.selections);
20478             }
20479         }
20480     },
20481
20482     /**
20483      * Returns true if the passed node is selected
20484      * @param {HTMLElement/Number} node The node or node index
20485      * @return {Boolean}
20486      */
20487     isSelected : function(node){
20488         var s = this.selections;
20489         if(s.length < 1){
20490             return false;
20491         }
20492         node = this.getNode(node);
20493         return s.indexOf(node) !== -1;
20494     },
20495
20496     /**
20497      * Selects nodes.
20498      * @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
20499      * @param {Boolean} keepExisting (optional) true to keep existing selections
20500      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20501      */
20502     select : function(nodeInfo, keepExisting, suppressEvent){
20503         if(nodeInfo instanceof Array){
20504             if(!keepExisting){
20505                 this.clearSelections(true);
20506             }
20507             for(var i = 0, len = nodeInfo.length; i < len; i++){
20508                 this.select(nodeInfo[i], true, true);
20509             }
20510             return;
20511         } 
20512         var node = this.getNode(nodeInfo);
20513         if(!node || this.isSelected(node)){
20514             return; // already selected.
20515         }
20516         if(!keepExisting){
20517             this.clearSelections(true);
20518         }
20519         
20520         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20521             Roo.fly(node).addClass(this.selectedClass);
20522             this.selections.push(node);
20523             if(!suppressEvent){
20524                 this.fireEvent("selectionchange", this, this.selections);
20525             }
20526         }
20527         
20528         
20529     },
20530       /**
20531      * Unselects nodes.
20532      * @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
20533      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20535      */
20536     unselect : function(nodeInfo, keepExisting, suppressEvent)
20537     {
20538         if(nodeInfo instanceof Array){
20539             Roo.each(this.selections, function(s) {
20540                 this.unselect(s, nodeInfo);
20541             }, this);
20542             return;
20543         }
20544         var node = this.getNode(nodeInfo);
20545         if(!node || !this.isSelected(node)){
20546             //Roo.log("not selected");
20547             return; // not selected.
20548         }
20549         // fireevent???
20550         var ns = [];
20551         Roo.each(this.selections, function(s) {
20552             if (s == node ) {
20553                 Roo.fly(node).removeClass(this.selectedClass);
20554
20555                 return;
20556             }
20557             ns.push(s);
20558         },this);
20559         
20560         this.selections= ns;
20561         this.fireEvent("selectionchange", this, this.selections);
20562     },
20563
20564     /**
20565      * Gets a template node.
20566      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20567      * @return {HTMLElement} The node or null if it wasn't found
20568      */
20569     getNode : function(nodeInfo){
20570         if(typeof nodeInfo == "string"){
20571             return document.getElementById(nodeInfo);
20572         }else if(typeof nodeInfo == "number"){
20573             return this.nodes[nodeInfo];
20574         }
20575         return nodeInfo;
20576     },
20577
20578     /**
20579      * Gets a range template nodes.
20580      * @param {Number} startIndex
20581      * @param {Number} endIndex
20582      * @return {Array} An array of nodes
20583      */
20584     getNodes : function(start, end){
20585         var ns = this.nodes;
20586         start = start || 0;
20587         end = typeof end == "undefined" ? ns.length - 1 : end;
20588         var nodes = [];
20589         if(start <= end){
20590             for(var i = start; i <= end; i++){
20591                 nodes.push(ns[i]);
20592             }
20593         } else{
20594             for(var i = start; i >= end; i--){
20595                 nodes.push(ns[i]);
20596             }
20597         }
20598         return nodes;
20599     },
20600
20601     /**
20602      * Finds the index of the passed node
20603      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20604      * @return {Number} The index of the node or -1
20605      */
20606     indexOf : function(node){
20607         node = this.getNode(node);
20608         if(typeof node.nodeIndex == "number"){
20609             return node.nodeIndex;
20610         }
20611         var ns = this.nodes;
20612         for(var i = 0, len = ns.length; i < len; i++){
20613             if(ns[i] == node){
20614                 return i;
20615             }
20616         }
20617         return -1;
20618     }
20619 });
20620 /*
20621  * - LGPL
20622  *
20623  * based on jquery fullcalendar
20624  * 
20625  */
20626
20627 Roo.bootstrap = Roo.bootstrap || {};
20628 /**
20629  * @class Roo.bootstrap.Calendar
20630  * @extends Roo.bootstrap.Component
20631  * Bootstrap Calendar class
20632  * @cfg {Boolean} loadMask (true|false) default false
20633  * @cfg {Object} header generate the user specific header of the calendar, default false
20634
20635  * @constructor
20636  * Create a new Container
20637  * @param {Object} config The config object
20638  */
20639
20640
20641
20642 Roo.bootstrap.Calendar = function(config){
20643     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20644      this.addEvents({
20645         /**
20646              * @event select
20647              * Fires when a date is selected
20648              * @param {DatePicker} this
20649              * @param {Date} date The selected date
20650              */
20651         'select': true,
20652         /**
20653              * @event monthchange
20654              * Fires when the displayed month changes 
20655              * @param {DatePicker} this
20656              * @param {Date} date The selected month
20657              */
20658         'monthchange': true,
20659         /**
20660              * @event evententer
20661              * Fires when mouse over an event
20662              * @param {Calendar} this
20663              * @param {event} Event
20664              */
20665         'evententer': true,
20666         /**
20667              * @event eventleave
20668              * Fires when the mouse leaves an
20669              * @param {Calendar} this
20670              * @param {event}
20671              */
20672         'eventleave': true,
20673         /**
20674              * @event eventclick
20675              * Fires when the mouse click an
20676              * @param {Calendar} this
20677              * @param {event}
20678              */
20679         'eventclick': true
20680         
20681     });
20682
20683 };
20684
20685 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20686     
20687           /**
20688      * @cfg {Roo.data.Store} store
20689      * The data source for the calendar
20690      */
20691         store : false,
20692      /**
20693      * @cfg {Number} startDay
20694      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20695      */
20696     startDay : 0,
20697     
20698     loadMask : false,
20699     
20700     header : false,
20701       
20702     getAutoCreate : function(){
20703         
20704         
20705         var fc_button = function(name, corner, style, content ) {
20706             return Roo.apply({},{
20707                 tag : 'span',
20708                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20709                          (corner.length ?
20710                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20711                             ''
20712                         ),
20713                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20714                 unselectable: 'on'
20715             });
20716         };
20717         
20718         var header = {};
20719         
20720         if(!this.header){
20721             header = {
20722                 tag : 'table',
20723                 cls : 'fc-header',
20724                 style : 'width:100%',
20725                 cn : [
20726                     {
20727                         tag: 'tr',
20728                         cn : [
20729                             {
20730                                 tag : 'td',
20731                                 cls : 'fc-header-left',
20732                                 cn : [
20733                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20734                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20735                                     { tag: 'span', cls: 'fc-header-space' },
20736                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20737
20738
20739                                 ]
20740                             },
20741
20742                             {
20743                                 tag : 'td',
20744                                 cls : 'fc-header-center',
20745                                 cn : [
20746                                     {
20747                                         tag: 'span',
20748                                         cls: 'fc-header-title',
20749                                         cn : {
20750                                             tag: 'H2',
20751                                             html : 'month / year'
20752                                         }
20753                                     }
20754
20755                                 ]
20756                             },
20757                             {
20758                                 tag : 'td',
20759                                 cls : 'fc-header-right',
20760                                 cn : [
20761                               /*      fc_button('month', 'left', '', 'month' ),
20762                                     fc_button('week', '', '', 'week' ),
20763                                     fc_button('day', 'right', '', 'day' )
20764                                 */    
20765
20766                                 ]
20767                             }
20768
20769                         ]
20770                     }
20771                 ]
20772             };
20773         }
20774         
20775         header = this.header;
20776         
20777        
20778         var cal_heads = function() {
20779             var ret = [];
20780             // fixme - handle this.
20781             
20782             for (var i =0; i < Date.dayNames.length; i++) {
20783                 var d = Date.dayNames[i];
20784                 ret.push({
20785                     tag: 'th',
20786                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20787                     html : d.substring(0,3)
20788                 });
20789                 
20790             }
20791             ret[0].cls += ' fc-first';
20792             ret[6].cls += ' fc-last';
20793             return ret;
20794         };
20795         var cal_cell = function(n) {
20796             return  {
20797                 tag: 'td',
20798                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20799                 cn : [
20800                     {
20801                         cn : [
20802                             {
20803                                 cls: 'fc-day-number',
20804                                 html: 'D'
20805                             },
20806                             {
20807                                 cls: 'fc-day-content',
20808                              
20809                                 cn : [
20810                                      {
20811                                         style: 'position: relative;' // height: 17px;
20812                                     }
20813                                 ]
20814                             }
20815                             
20816                             
20817                         ]
20818                     }
20819                 ]
20820                 
20821             }
20822         };
20823         var cal_rows = function() {
20824             
20825             var ret = [];
20826             for (var r = 0; r < 6; r++) {
20827                 var row= {
20828                     tag : 'tr',
20829                     cls : 'fc-week',
20830                     cn : []
20831                 };
20832                 
20833                 for (var i =0; i < Date.dayNames.length; i++) {
20834                     var d = Date.dayNames[i];
20835                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20836
20837                 }
20838                 row.cn[0].cls+=' fc-first';
20839                 row.cn[0].cn[0].style = 'min-height:90px';
20840                 row.cn[6].cls+=' fc-last';
20841                 ret.push(row);
20842                 
20843             }
20844             ret[0].cls += ' fc-first';
20845             ret[4].cls += ' fc-prev-last';
20846             ret[5].cls += ' fc-last';
20847             return ret;
20848             
20849         };
20850         
20851         var cal_table = {
20852             tag: 'table',
20853             cls: 'fc-border-separate',
20854             style : 'width:100%',
20855             cellspacing  : 0,
20856             cn : [
20857                 { 
20858                     tag: 'thead',
20859                     cn : [
20860                         { 
20861                             tag: 'tr',
20862                             cls : 'fc-first fc-last',
20863                             cn : cal_heads()
20864                         }
20865                     ]
20866                 },
20867                 { 
20868                     tag: 'tbody',
20869                     cn : cal_rows()
20870                 }
20871                   
20872             ]
20873         };
20874          
20875          var cfg = {
20876             cls : 'fc fc-ltr',
20877             cn : [
20878                 header,
20879                 {
20880                     cls : 'fc-content',
20881                     style : "position: relative;",
20882                     cn : [
20883                         {
20884                             cls : 'fc-view fc-view-month fc-grid',
20885                             style : 'position: relative',
20886                             unselectable : 'on',
20887                             cn : [
20888                                 {
20889                                     cls : 'fc-event-container',
20890                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20891                                 },
20892                                 cal_table
20893                             ]
20894                         }
20895                     ]
20896     
20897                 }
20898            ] 
20899             
20900         };
20901         
20902          
20903         
20904         return cfg;
20905     },
20906     
20907     
20908     initEvents : function()
20909     {
20910         if(!this.store){
20911             throw "can not find store for calendar";
20912         }
20913         
20914         var mark = {
20915             tag: "div",
20916             cls:"x-dlg-mask",
20917             style: "text-align:center",
20918             cn: [
20919                 {
20920                     tag: "div",
20921                     style: "background-color:white;width:50%;margin:250 auto",
20922                     cn: [
20923                         {
20924                             tag: "img",
20925                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20926                         },
20927                         {
20928                             tag: "span",
20929                             html: "Loading"
20930                         }
20931                         
20932                     ]
20933                 }
20934             ]
20935         };
20936         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20937         
20938         var size = this.el.select('.fc-content', true).first().getSize();
20939         this.maskEl.setSize(size.width, size.height);
20940         this.maskEl.enableDisplayMode("block");
20941         if(!this.loadMask){
20942             this.maskEl.hide();
20943         }
20944         
20945         this.store = Roo.factory(this.store, Roo.data);
20946         this.store.on('load', this.onLoad, this);
20947         this.store.on('beforeload', this.onBeforeLoad, this);
20948         
20949         this.resize();
20950         
20951         this.cells = this.el.select('.fc-day',true);
20952         //Roo.log(this.cells);
20953         this.textNodes = this.el.query('.fc-day-number');
20954         this.cells.addClassOnOver('fc-state-hover');
20955         
20956         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20957         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20958         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20959         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20960         
20961         this.on('monthchange', this.onMonthChange, this);
20962         
20963         this.update(new Date().clearTime());
20964     },
20965     
20966     resize : function() {
20967         var sz  = this.el.getSize();
20968         
20969         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20970         this.el.select('.fc-day-content div',true).setHeight(34);
20971     },
20972     
20973     
20974     // private
20975     showPrevMonth : function(e){
20976         this.update(this.activeDate.add("mo", -1));
20977     },
20978     showToday : function(e){
20979         this.update(new Date().clearTime());
20980     },
20981     // private
20982     showNextMonth : function(e){
20983         this.update(this.activeDate.add("mo", 1));
20984     },
20985
20986     // private
20987     showPrevYear : function(){
20988         this.update(this.activeDate.add("y", -1));
20989     },
20990
20991     // private
20992     showNextYear : function(){
20993         this.update(this.activeDate.add("y", 1));
20994     },
20995
20996     
20997    // private
20998     update : function(date)
20999     {
21000         var vd = this.activeDate;
21001         this.activeDate = date;
21002 //        if(vd && this.el){
21003 //            var t = date.getTime();
21004 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21005 //                Roo.log('using add remove');
21006 //                
21007 //                this.fireEvent('monthchange', this, date);
21008 //                
21009 //                this.cells.removeClass("fc-state-highlight");
21010 //                this.cells.each(function(c){
21011 //                   if(c.dateValue == t){
21012 //                       c.addClass("fc-state-highlight");
21013 //                       setTimeout(function(){
21014 //                            try{c.dom.firstChild.focus();}catch(e){}
21015 //                       }, 50);
21016 //                       return false;
21017 //                   }
21018 //                   return true;
21019 //                });
21020 //                return;
21021 //            }
21022 //        }
21023         
21024         var days = date.getDaysInMonth();
21025         
21026         var firstOfMonth = date.getFirstDateOfMonth();
21027         var startingPos = firstOfMonth.getDay()-this.startDay;
21028         
21029         if(startingPos < this.startDay){
21030             startingPos += 7;
21031         }
21032         
21033         var pm = date.add(Date.MONTH, -1);
21034         var prevStart = pm.getDaysInMonth()-startingPos;
21035 //        
21036         this.cells = this.el.select('.fc-day',true);
21037         this.textNodes = this.el.query('.fc-day-number');
21038         this.cells.addClassOnOver('fc-state-hover');
21039         
21040         var cells = this.cells.elements;
21041         var textEls = this.textNodes;
21042         
21043         Roo.each(cells, function(cell){
21044             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21045         });
21046         
21047         days += startingPos;
21048
21049         // convert everything to numbers so it's fast
21050         var day = 86400000;
21051         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21052         //Roo.log(d);
21053         //Roo.log(pm);
21054         //Roo.log(prevStart);
21055         
21056         var today = new Date().clearTime().getTime();
21057         var sel = date.clearTime().getTime();
21058         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21059         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21060         var ddMatch = this.disabledDatesRE;
21061         var ddText = this.disabledDatesText;
21062         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21063         var ddaysText = this.disabledDaysText;
21064         var format = this.format;
21065         
21066         var setCellClass = function(cal, cell){
21067             cell.row = 0;
21068             cell.events = [];
21069             cell.more = [];
21070             //Roo.log('set Cell Class');
21071             cell.title = "";
21072             var t = d.getTime();
21073             
21074             //Roo.log(d);
21075             
21076             cell.dateValue = t;
21077             if(t == today){
21078                 cell.className += " fc-today";
21079                 cell.className += " fc-state-highlight";
21080                 cell.title = cal.todayText;
21081             }
21082             if(t == sel){
21083                 // disable highlight in other month..
21084                 //cell.className += " fc-state-highlight";
21085                 
21086             }
21087             // disabling
21088             if(t < min) {
21089                 cell.className = " fc-state-disabled";
21090                 cell.title = cal.minText;
21091                 return;
21092             }
21093             if(t > max) {
21094                 cell.className = " fc-state-disabled";
21095                 cell.title = cal.maxText;
21096                 return;
21097             }
21098             if(ddays){
21099                 if(ddays.indexOf(d.getDay()) != -1){
21100                     cell.title = ddaysText;
21101                     cell.className = " fc-state-disabled";
21102                 }
21103             }
21104             if(ddMatch && format){
21105                 var fvalue = d.dateFormat(format);
21106                 if(ddMatch.test(fvalue)){
21107                     cell.title = ddText.replace("%0", fvalue);
21108                     cell.className = " fc-state-disabled";
21109                 }
21110             }
21111             
21112             if (!cell.initialClassName) {
21113                 cell.initialClassName = cell.dom.className;
21114             }
21115             
21116             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21117         };
21118
21119         var i = 0;
21120         
21121         for(; i < startingPos; i++) {
21122             textEls[i].innerHTML = (++prevStart);
21123             d.setDate(d.getDate()+1);
21124             
21125             cells[i].className = "fc-past fc-other-month";
21126             setCellClass(this, cells[i]);
21127         }
21128         
21129         var intDay = 0;
21130         
21131         for(; i < days; i++){
21132             intDay = i - startingPos + 1;
21133             textEls[i].innerHTML = (intDay);
21134             d.setDate(d.getDate()+1);
21135             
21136             cells[i].className = ''; // "x-date-active";
21137             setCellClass(this, cells[i]);
21138         }
21139         var extraDays = 0;
21140         
21141         for(; i < 42; i++) {
21142             textEls[i].innerHTML = (++extraDays);
21143             d.setDate(d.getDate()+1);
21144             
21145             cells[i].className = "fc-future fc-other-month";
21146             setCellClass(this, cells[i]);
21147         }
21148         
21149         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21150         
21151         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21152         
21153         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21154         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21155         
21156         if(totalRows != 6){
21157             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21158             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21159         }
21160         
21161         this.fireEvent('monthchange', this, date);
21162         
21163         
21164         /*
21165         if(!this.internalRender){
21166             var main = this.el.dom.firstChild;
21167             var w = main.offsetWidth;
21168             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21169             Roo.fly(main).setWidth(w);
21170             this.internalRender = true;
21171             // opera does not respect the auto grow header center column
21172             // then, after it gets a width opera refuses to recalculate
21173             // without a second pass
21174             if(Roo.isOpera && !this.secondPass){
21175                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21176                 this.secondPass = true;
21177                 this.update.defer(10, this, [date]);
21178             }
21179         }
21180         */
21181         
21182     },
21183     
21184     findCell : function(dt) {
21185         dt = dt.clearTime().getTime();
21186         var ret = false;
21187         this.cells.each(function(c){
21188             //Roo.log("check " +c.dateValue + '?=' + dt);
21189             if(c.dateValue == dt){
21190                 ret = c;
21191                 return false;
21192             }
21193             return true;
21194         });
21195         
21196         return ret;
21197     },
21198     
21199     findCells : function(ev) {
21200         var s = ev.start.clone().clearTime().getTime();
21201        // Roo.log(s);
21202         var e= ev.end.clone().clearTime().getTime();
21203        // Roo.log(e);
21204         var ret = [];
21205         this.cells.each(function(c){
21206              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21207             
21208             if(c.dateValue > e){
21209                 return ;
21210             }
21211             if(c.dateValue < s){
21212                 return ;
21213             }
21214             ret.push(c);
21215         });
21216         
21217         return ret;    
21218     },
21219     
21220 //    findBestRow: function(cells)
21221 //    {
21222 //        var ret = 0;
21223 //        
21224 //        for (var i =0 ; i < cells.length;i++) {
21225 //            ret  = Math.max(cells[i].rows || 0,ret);
21226 //        }
21227 //        return ret;
21228 //        
21229 //    },
21230     
21231     
21232     addItem : function(ev)
21233     {
21234         // look for vertical location slot in
21235         var cells = this.findCells(ev);
21236         
21237 //        ev.row = this.findBestRow(cells);
21238         
21239         // work out the location.
21240         
21241         var crow = false;
21242         var rows = [];
21243         for(var i =0; i < cells.length; i++) {
21244             
21245             cells[i].row = cells[0].row;
21246             
21247             if(i == 0){
21248                 cells[i].row = cells[i].row + 1;
21249             }
21250             
21251             if (!crow) {
21252                 crow = {
21253                     start : cells[i],
21254                     end :  cells[i]
21255                 };
21256                 continue;
21257             }
21258             if (crow.start.getY() == cells[i].getY()) {
21259                 // on same row.
21260                 crow.end = cells[i];
21261                 continue;
21262             }
21263             // different row.
21264             rows.push(crow);
21265             crow = {
21266                 start: cells[i],
21267                 end : cells[i]
21268             };
21269             
21270         }
21271         
21272         rows.push(crow);
21273         ev.els = [];
21274         ev.rows = rows;
21275         ev.cells = cells;
21276         
21277         cells[0].events.push(ev);
21278         
21279         this.calevents.push(ev);
21280     },
21281     
21282     clearEvents: function() {
21283         
21284         if(!this.calevents){
21285             return;
21286         }
21287         
21288         Roo.each(this.cells.elements, function(c){
21289             c.row = 0;
21290             c.events = [];
21291             c.more = [];
21292         });
21293         
21294         Roo.each(this.calevents, function(e) {
21295             Roo.each(e.els, function(el) {
21296                 el.un('mouseenter' ,this.onEventEnter, this);
21297                 el.un('mouseleave' ,this.onEventLeave, this);
21298                 el.remove();
21299             },this);
21300         },this);
21301         
21302         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21303             e.remove();
21304         });
21305         
21306     },
21307     
21308     renderEvents: function()
21309     {   
21310         var _this = this;
21311         
21312         this.cells.each(function(c) {
21313             
21314             if(c.row < 5){
21315                 return;
21316             }
21317             
21318             var ev = c.events;
21319             
21320             var r = 4;
21321             if(c.row != c.events.length){
21322                 r = 4 - (4 - (c.row - c.events.length));
21323             }
21324             
21325             c.events = ev.slice(0, r);
21326             c.more = ev.slice(r);
21327             
21328             if(c.more.length && c.more.length == 1){
21329                 c.events.push(c.more.pop());
21330             }
21331             
21332             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21333             
21334         });
21335             
21336         this.cells.each(function(c) {
21337             
21338             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21339             
21340             
21341             for (var e = 0; e < c.events.length; e++){
21342                 var ev = c.events[e];
21343                 var rows = ev.rows;
21344                 
21345                 for(var i = 0; i < rows.length; i++) {
21346                 
21347                     // how many rows should it span..
21348
21349                     var  cfg = {
21350                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21351                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21352
21353                         unselectable : "on",
21354                         cn : [
21355                             {
21356                                 cls: 'fc-event-inner',
21357                                 cn : [
21358     //                                {
21359     //                                  tag:'span',
21360     //                                  cls: 'fc-event-time',
21361     //                                  html : cells.length > 1 ? '' : ev.time
21362     //                                },
21363                                     {
21364                                       tag:'span',
21365                                       cls: 'fc-event-title',
21366                                       html : String.format('{0}', ev.title)
21367                                     }
21368
21369
21370                                 ]
21371                             },
21372                             {
21373                                 cls: 'ui-resizable-handle ui-resizable-e',
21374                                 html : '&nbsp;&nbsp;&nbsp'
21375                             }
21376
21377                         ]
21378                     };
21379
21380                     if (i == 0) {
21381                         cfg.cls += ' fc-event-start';
21382                     }
21383                     if ((i+1) == rows.length) {
21384                         cfg.cls += ' fc-event-end';
21385                     }
21386
21387                     var ctr = _this.el.select('.fc-event-container',true).first();
21388                     var cg = ctr.createChild(cfg);
21389
21390                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21391                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21392
21393                     var r = (c.more.length) ? 1 : 0;
21394                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21395                     cg.setWidth(ebox.right - sbox.x -2);
21396
21397                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21398                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21399                     cg.on('click', _this.onEventClick, _this, ev);
21400
21401                     ev.els.push(cg);
21402                     
21403                 }
21404                 
21405             }
21406             
21407             
21408             if(c.more.length){
21409                 var  cfg = {
21410                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21411                     style : 'position: absolute',
21412                     unselectable : "on",
21413                     cn : [
21414                         {
21415                             cls: 'fc-event-inner',
21416                             cn : [
21417                                 {
21418                                   tag:'span',
21419                                   cls: 'fc-event-title',
21420                                   html : 'More'
21421                                 }
21422
21423
21424                             ]
21425                         },
21426                         {
21427                             cls: 'ui-resizable-handle ui-resizable-e',
21428                             html : '&nbsp;&nbsp;&nbsp'
21429                         }
21430
21431                     ]
21432                 };
21433
21434                 var ctr = _this.el.select('.fc-event-container',true).first();
21435                 var cg = ctr.createChild(cfg);
21436
21437                 var sbox = c.select('.fc-day-content',true).first().getBox();
21438                 var ebox = c.select('.fc-day-content',true).first().getBox();
21439                 //Roo.log(cg);
21440                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21441                 cg.setWidth(ebox.right - sbox.x -2);
21442
21443                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21444                 
21445             }
21446             
21447         });
21448         
21449         
21450         
21451     },
21452     
21453     onEventEnter: function (e, el,event,d) {
21454         this.fireEvent('evententer', this, el, event);
21455     },
21456     
21457     onEventLeave: function (e, el,event,d) {
21458         this.fireEvent('eventleave', this, el, event);
21459     },
21460     
21461     onEventClick: function (e, el,event,d) {
21462         this.fireEvent('eventclick', this, el, event);
21463     },
21464     
21465     onMonthChange: function () {
21466         this.store.load();
21467     },
21468     
21469     onMoreEventClick: function(e, el, more)
21470     {
21471         var _this = this;
21472         
21473         this.calpopover.placement = 'right';
21474         this.calpopover.setTitle('More');
21475         
21476         this.calpopover.setContent('');
21477         
21478         var ctr = this.calpopover.el.select('.popover-content', true).first();
21479         
21480         Roo.each(more, function(m){
21481             var cfg = {
21482                 cls : 'fc-event-hori fc-event-draggable',
21483                 html : m.title
21484             };
21485             var cg = ctr.createChild(cfg);
21486             
21487             cg.on('click', _this.onEventClick, _this, m);
21488         });
21489         
21490         this.calpopover.show(el);
21491         
21492         
21493     },
21494     
21495     onLoad: function () 
21496     {   
21497         this.calevents = [];
21498         var cal = this;
21499         
21500         if(this.store.getCount() > 0){
21501             this.store.data.each(function(d){
21502                cal.addItem({
21503                     id : d.data.id,
21504                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21505                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21506                     time : d.data.start_time,
21507                     title : d.data.title,
21508                     description : d.data.description,
21509                     venue : d.data.venue
21510                 });
21511             });
21512         }
21513         
21514         this.renderEvents();
21515         
21516         if(this.calevents.length && this.loadMask){
21517             this.maskEl.hide();
21518         }
21519     },
21520     
21521     onBeforeLoad: function()
21522     {
21523         this.clearEvents();
21524         if(this.loadMask){
21525             this.maskEl.show();
21526         }
21527     }
21528 });
21529
21530  
21531  /*
21532  * - LGPL
21533  *
21534  * element
21535  * 
21536  */
21537
21538 /**
21539  * @class Roo.bootstrap.Popover
21540  * @extends Roo.bootstrap.Component
21541  * @parent none builder
21542  * @children Roo.bootstrap.Component
21543  * Bootstrap Popover class
21544  * @cfg {String} html contents of the popover   (or false to use children..)
21545  * @cfg {String} title of popover (or false to hide)
21546  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21547  * @cfg {String} trigger click || hover (or false to trigger manually)
21548  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21549  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21550  *      - if false and it has a 'parent' then it will be automatically added to that element
21551  *      - if string - Roo.get  will be called 
21552  * @cfg {Number} delay - delay before showing
21553  
21554  * @constructor
21555  * Create a new Popover
21556  * @param {Object} config The config object
21557  */
21558
21559 Roo.bootstrap.Popover = function(config){
21560     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21561     
21562     this.addEvents({
21563         // raw events
21564          /**
21565          * @event show
21566          * After the popover show
21567          * 
21568          * @param {Roo.bootstrap.Popover} this
21569          */
21570         "show" : true,
21571         /**
21572          * @event hide
21573          * After the popover hide
21574          * 
21575          * @param {Roo.bootstrap.Popover} this
21576          */
21577         "hide" : true
21578     });
21579 };
21580
21581 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21582     
21583     title: false,
21584     html: false,
21585     
21586     placement : 'right',
21587     trigger : 'hover', // hover
21588     modal : false,
21589     delay : 0,
21590     
21591     over: false,
21592     
21593     can_build_overlaid : false,
21594     
21595     maskEl : false, // the mask element
21596     headerEl : false,
21597     contentEl : false,
21598     alignEl : false, // when show is called with an element - this get's stored.
21599     
21600     getChildContainer : function()
21601     {
21602         return this.contentEl;
21603         
21604     },
21605     getPopoverHeader : function()
21606     {
21607         this.title = true; // flag not to hide it..
21608         this.headerEl.addClass('p-0');
21609         return this.headerEl
21610     },
21611     
21612     
21613     getAutoCreate : function(){
21614          
21615         var cfg = {
21616            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21617            style: 'display:block',
21618            cn : [
21619                 {
21620                     cls : 'arrow'
21621                 },
21622                 {
21623                     cls : 'popover-inner ',
21624                     cn : [
21625                         {
21626                             tag: 'h3',
21627                             cls: 'popover-title popover-header',
21628                             html : this.title === false ? '' : this.title
21629                         },
21630                         {
21631                             cls : 'popover-content popover-body '  + (this.cls || ''),
21632                             html : this.html || ''
21633                         }
21634                     ]
21635                     
21636                 }
21637            ]
21638         };
21639         
21640         return cfg;
21641     },
21642     /**
21643      * @param {string} the title
21644      */
21645     setTitle: function(str)
21646     {
21647         this.title = str;
21648         if (this.el) {
21649             this.headerEl.dom.innerHTML = str;
21650         }
21651         
21652     },
21653     /**
21654      * @param {string} the body content
21655      */
21656     setContent: function(str)
21657     {
21658         this.html = str;
21659         if (this.contentEl) {
21660             this.contentEl.dom.innerHTML = str;
21661         }
21662         
21663     },
21664     // as it get's added to the bottom of the page.
21665     onRender : function(ct, position)
21666     {
21667         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21668         
21669         
21670         
21671         if(!this.el){
21672             var cfg = Roo.apply({},  this.getAutoCreate());
21673             cfg.id = Roo.id();
21674             
21675             if (this.cls) {
21676                 cfg.cls += ' ' + this.cls;
21677             }
21678             if (this.style) {
21679                 cfg.style = this.style;
21680             }
21681             //Roo.log("adding to ");
21682             this.el = Roo.get(document.body).createChild(cfg, position);
21683 //            Roo.log(this.el);
21684         }
21685         
21686         this.contentEl = this.el.select('.popover-content',true).first();
21687         this.headerEl =  this.el.select('.popover-title',true).first();
21688         
21689         var nitems = [];
21690         if(typeof(this.items) != 'undefined'){
21691             var items = this.items;
21692             delete this.items;
21693
21694             for(var i =0;i < items.length;i++) {
21695                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21696             }
21697         }
21698
21699         this.items = nitems;
21700         
21701         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21702         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21703         
21704         
21705         
21706         this.initEvents();
21707     },
21708     
21709     resizeMask : function()
21710     {
21711         this.maskEl.setSize(
21712             Roo.lib.Dom.getViewWidth(true),
21713             Roo.lib.Dom.getViewHeight(true)
21714         );
21715     },
21716     
21717     initEvents : function()
21718     {
21719         
21720         if (!this.modal) { 
21721             Roo.bootstrap.Popover.register(this);
21722         }
21723          
21724         this.arrowEl = this.el.select('.arrow',true).first();
21725         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21726         this.el.enableDisplayMode('block');
21727         this.el.hide();
21728  
21729         
21730         if (this.over === false && !this.parent()) {
21731             return; 
21732         }
21733         if (this.triggers === false) {
21734             return;
21735         }
21736          
21737         // support parent
21738         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21739         var triggers = this.trigger ? this.trigger.split(' ') : [];
21740         Roo.each(triggers, function(trigger) {
21741         
21742             if (trigger == 'click') {
21743                 on_el.on('click', this.toggle, this);
21744             } else if (trigger != 'manual') {
21745                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21746                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21747       
21748                 on_el.on(eventIn  ,this.enter, this);
21749                 on_el.on(eventOut, this.leave, this);
21750             }
21751         }, this);
21752     },
21753     
21754     
21755     // private
21756     timeout : null,
21757     hoverState : null,
21758     
21759     toggle : function () {
21760         this.hoverState == 'in' ? this.leave() : this.enter();
21761     },
21762     
21763     enter : function () {
21764         
21765         clearTimeout(this.timeout);
21766     
21767         this.hoverState = 'in';
21768     
21769         if (!this.delay || !this.delay.show) {
21770             this.show();
21771             return;
21772         }
21773         var _t = this;
21774         this.timeout = setTimeout(function () {
21775             if (_t.hoverState == 'in') {
21776                 _t.show();
21777             }
21778         }, this.delay.show)
21779     },
21780     
21781     leave : function() {
21782         clearTimeout(this.timeout);
21783     
21784         this.hoverState = 'out';
21785     
21786         if (!this.delay || !this.delay.hide) {
21787             this.hide();
21788             return;
21789         }
21790         var _t = this;
21791         this.timeout = setTimeout(function () {
21792             if (_t.hoverState == 'out') {
21793                 _t.hide();
21794             }
21795         }, this.delay.hide)
21796     },
21797     
21798     /**
21799      * update the position of the dialog
21800      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21801      * 
21802      *
21803      */
21804     
21805     doAlign : function()
21806     {
21807         
21808         if (this.alignEl) {
21809             this.updatePosition(this.placement, true);
21810              
21811         } else {
21812             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21813             var es = this.el.getSize();
21814             var x = Roo.lib.Dom.getViewWidth()/2;
21815             var y = Roo.lib.Dom.getViewHeight()/2;
21816             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21817             
21818         }
21819
21820          
21821          
21822         
21823         
21824     },
21825     
21826     /**
21827      * Show the popover
21828      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21829      * @param {string} (left|right|top|bottom) position
21830      */
21831     show : function (on_el, placement)
21832     {
21833         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21834         on_el = on_el || false; // default to false
21835          
21836         if (!on_el) {
21837             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21838                 on_el = this.parent().el;
21839             } else if (this.over) {
21840                 on_el = Roo.get(this.over);
21841             }
21842             
21843         }
21844         
21845         this.alignEl = Roo.get( on_el );
21846
21847         if (!this.el) {
21848             this.render(document.body);
21849         }
21850         
21851         
21852          
21853         
21854         if (this.title === false) {
21855             this.headerEl.hide();
21856         }
21857         
21858        
21859         this.el.show();
21860         this.el.dom.style.display = 'block';
21861          
21862         this.doAlign();
21863         
21864         //var arrow = this.el.select('.arrow',true).first();
21865         //arrow.set(align[2], 
21866         
21867         this.el.addClass('in');
21868         
21869          
21870         
21871         this.hoverState = 'in';
21872         
21873         if (this.modal) {
21874             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21875             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21876             this.maskEl.dom.style.display = 'block';
21877             this.maskEl.addClass('show');
21878         }
21879         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21880  
21881         this.fireEvent('show', this);
21882         
21883     },
21884     /**
21885      * fire this manually after loading a grid in the table for example
21886      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21887      * @param {Boolean} try and move it if we cant get right position.
21888      */
21889     updatePosition : function(placement, try_move)
21890     {
21891         // allow for calling with no parameters
21892         placement = placement   ? placement :  this.placement;
21893         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21894         
21895         this.el.removeClass([
21896             'fade','top','bottom', 'left', 'right','in',
21897             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21898         ]);
21899         this.el.addClass(placement + ' bs-popover-' + placement);
21900         
21901         if (!this.alignEl ) {
21902             return false;
21903         }
21904         
21905         switch (placement) {
21906             case 'right':
21907                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21908                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21909                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21910                     //normal display... or moved up/down.
21911                     this.el.setXY(offset);
21912                     var xy = this.alignEl.getAnchorXY('tr', false);
21913                     xy[0]+=2;xy[1]+=5;
21914                     this.arrowEl.setXY(xy);
21915                     return true;
21916                 }
21917                 // continue through...
21918                 return this.updatePosition('left', false);
21919                 
21920             
21921             case 'left':
21922                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21923                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21924                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21925                     //normal display... or moved up/down.
21926                     this.el.setXY(offset);
21927                     var xy = this.alignEl.getAnchorXY('tl', false);
21928                     xy[0]-=10;xy[1]+=5; // << fix me
21929                     this.arrowEl.setXY(xy);
21930                     return true;
21931                 }
21932                 // call self...
21933                 return this.updatePosition('right', false);
21934             
21935             case 'top':
21936                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21937                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21938                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21939                     //normal display... or moved up/down.
21940                     this.el.setXY(offset);
21941                     var xy = this.alignEl.getAnchorXY('t', false);
21942                     xy[1]-=10; // << fix me
21943                     this.arrowEl.setXY(xy);
21944                     return true;
21945                 }
21946                 // fall through
21947                return this.updatePosition('bottom', false);
21948             
21949             case 'bottom':
21950                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21951                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21952                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21953                     //normal display... or moved up/down.
21954                     this.el.setXY(offset);
21955                     var xy = this.alignEl.getAnchorXY('b', false);
21956                      xy[1]+=2; // << fix me
21957                     this.arrowEl.setXY(xy);
21958                     return true;
21959                 }
21960                 // fall through
21961                 return this.updatePosition('top', false);
21962                 
21963             
21964         }
21965         
21966         
21967         return false;
21968     },
21969     
21970     hide : function()
21971     {
21972         this.el.setXY([0,0]);
21973         this.el.removeClass('in');
21974         this.el.hide();
21975         this.hoverState = null;
21976         this.maskEl.hide(); // always..
21977         this.fireEvent('hide', this);
21978     }
21979     
21980 });
21981
21982
21983 Roo.apply(Roo.bootstrap.Popover, {
21984
21985     alignment : {
21986         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21987         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21988         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21989         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21990     },
21991     
21992     zIndex : 20001,
21993
21994     clickHander : false,
21995     
21996     
21997
21998     onMouseDown : function(e)
21999     {
22000         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22001             /// what is nothing is showing..
22002             this.hideAll();
22003         }
22004          
22005     },
22006     
22007     
22008     popups : [],
22009     
22010     register : function(popup)
22011     {
22012         if (!Roo.bootstrap.Popover.clickHandler) {
22013             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22014         }
22015         // hide other popups.
22016         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22017         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22018         this.hideAll(); //<< why?
22019         //this.popups.push(popup);
22020     },
22021     hideAll : function()
22022     {
22023         this.popups.forEach(function(p) {
22024             p.hide();
22025         });
22026     },
22027     onShow : function() {
22028         Roo.bootstrap.Popover.popups.push(this);
22029     },
22030     onHide : function() {
22031         Roo.bootstrap.Popover.popups.remove(this);
22032     } 
22033
22034 });
22035 /**
22036  * @class Roo.bootstrap.PopoverNav
22037  * @extends Roo.bootstrap.nav.Simplebar
22038  * @parent Roo.bootstrap.Popover
22039  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22040  * @licence LGPL
22041  * Bootstrap Popover header navigation class
22042  * FIXME? should this go under nav?
22043  *
22044  * 
22045  * @constructor
22046  * Create a new Popover Header Navigation 
22047  * @param {Object} config The config object
22048  */
22049
22050 Roo.bootstrap.PopoverNav = function(config){
22051     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22052 };
22053
22054 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22055     
22056     
22057     container_method : 'getPopoverHeader' 
22058     
22059      
22060     
22061     
22062    
22063 });
22064
22065  
22066
22067  /*
22068  * - LGPL
22069  *
22070  * Progress
22071  * 
22072  */
22073
22074 /**
22075  * @class Roo.bootstrap.Progress
22076  * @extends Roo.bootstrap.Component
22077  * @children Roo.bootstrap.ProgressBar
22078  * Bootstrap Progress class
22079  * @cfg {Boolean} striped striped of the progress bar
22080  * @cfg {Boolean} active animated of the progress bar
22081  * 
22082  * 
22083  * @constructor
22084  * Create a new Progress
22085  * @param {Object} config The config object
22086  */
22087
22088 Roo.bootstrap.Progress = function(config){
22089     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22090 };
22091
22092 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22093     
22094     striped : false,
22095     active: false,
22096     
22097     getAutoCreate : function(){
22098         var cfg = {
22099             tag: 'div',
22100             cls: 'progress'
22101         };
22102         
22103         
22104         if(this.striped){
22105             cfg.cls += ' progress-striped';
22106         }
22107       
22108         if(this.active){
22109             cfg.cls += ' active';
22110         }
22111         
22112         
22113         return cfg;
22114     }
22115    
22116 });
22117
22118  
22119
22120  /*
22121  * - LGPL
22122  *
22123  * ProgressBar
22124  * 
22125  */
22126
22127 /**
22128  * @class Roo.bootstrap.ProgressBar
22129  * @extends Roo.bootstrap.Component
22130  * Bootstrap ProgressBar class
22131  * @cfg {Number} aria_valuenow aria-value now
22132  * @cfg {Number} aria_valuemin aria-value min
22133  * @cfg {Number} aria_valuemax aria-value max
22134  * @cfg {String} label label for the progress bar
22135  * @cfg {String} panel (success | info | warning | danger )
22136  * @cfg {String} role role of the progress bar
22137  * @cfg {String} sr_only text
22138  * 
22139  * 
22140  * @constructor
22141  * Create a new ProgressBar
22142  * @param {Object} config The config object
22143  */
22144
22145 Roo.bootstrap.ProgressBar = function(config){
22146     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22147 };
22148
22149 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22150     
22151     aria_valuenow : 0,
22152     aria_valuemin : 0,
22153     aria_valuemax : 100,
22154     label : false,
22155     panel : false,
22156     role : false,
22157     sr_only: false,
22158     
22159     getAutoCreate : function()
22160     {
22161         
22162         var cfg = {
22163             tag: 'div',
22164             cls: 'progress-bar',
22165             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22166         };
22167         
22168         if(this.sr_only){
22169             cfg.cn = {
22170                 tag: 'span',
22171                 cls: 'sr-only',
22172                 html: this.sr_only
22173             }
22174         }
22175         
22176         if(this.role){
22177             cfg.role = this.role;
22178         }
22179         
22180         if(this.aria_valuenow){
22181             cfg['aria-valuenow'] = this.aria_valuenow;
22182         }
22183         
22184         if(this.aria_valuemin){
22185             cfg['aria-valuemin'] = this.aria_valuemin;
22186         }
22187         
22188         if(this.aria_valuemax){
22189             cfg['aria-valuemax'] = this.aria_valuemax;
22190         }
22191         
22192         if(this.label && !this.sr_only){
22193             cfg.html = this.label;
22194         }
22195         
22196         if(this.panel){
22197             cfg.cls += ' progress-bar-' + this.panel;
22198         }
22199         
22200         return cfg;
22201     },
22202     
22203     update : function(aria_valuenow)
22204     {
22205         this.aria_valuenow = aria_valuenow;
22206         
22207         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22208     }
22209    
22210 });
22211
22212  
22213
22214  /**
22215  * @class Roo.bootstrap.TabGroup
22216  * @extends Roo.bootstrap.Column
22217  * @children Roo.bootstrap.TabPanel
22218  * Bootstrap Column class
22219  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22220  * @cfg {Boolean} carousel true to make the group behave like a carousel
22221  * @cfg {Boolean} bullets show bullets for the panels
22222  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22223  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22224  * @cfg {Boolean} showarrow (true|false) show arrow default true
22225  * 
22226  * @constructor
22227  * Create a new TabGroup
22228  * @param {Object} config The config object
22229  */
22230
22231 Roo.bootstrap.TabGroup = function(config){
22232     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22233     if (!this.navId) {
22234         this.navId = Roo.id();
22235     }
22236     this.tabs = [];
22237     Roo.bootstrap.TabGroup.register(this);
22238     
22239 };
22240
22241 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22242     
22243     carousel : false,
22244     transition : false,
22245     bullets : 0,
22246     timer : 0,
22247     autoslide : false,
22248     slideFn : false,
22249     slideOnTouch : false,
22250     showarrow : true,
22251     
22252     getAutoCreate : function()
22253     {
22254         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22255         
22256         cfg.cls += ' tab-content';
22257         
22258         if (this.carousel) {
22259             cfg.cls += ' carousel slide';
22260             
22261             cfg.cn = [{
22262                cls : 'carousel-inner',
22263                cn : []
22264             }];
22265         
22266             if(this.bullets  && !Roo.isTouch){
22267                 
22268                 var bullets = {
22269                     cls : 'carousel-bullets',
22270                     cn : []
22271                 };
22272                
22273                 if(this.bullets_cls){
22274                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22275                 }
22276                 
22277                 bullets.cn.push({
22278                     cls : 'clear'
22279                 });
22280                 
22281                 cfg.cn[0].cn.push(bullets);
22282             }
22283             
22284             if(this.showarrow){
22285                 cfg.cn[0].cn.push({
22286                     tag : 'div',
22287                     class : 'carousel-arrow',
22288                     cn : [
22289                         {
22290                             tag : 'div',
22291                             class : 'carousel-prev',
22292                             cn : [
22293                                 {
22294                                     tag : 'i',
22295                                     class : 'fa fa-chevron-left'
22296                                 }
22297                             ]
22298                         },
22299                         {
22300                             tag : 'div',
22301                             class : 'carousel-next',
22302                             cn : [
22303                                 {
22304                                     tag : 'i',
22305                                     class : 'fa fa-chevron-right'
22306                                 }
22307                             ]
22308                         }
22309                     ]
22310                 });
22311             }
22312             
22313         }
22314         
22315         return cfg;
22316     },
22317     
22318     initEvents:  function()
22319     {
22320 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22321 //            this.el.on("touchstart", this.onTouchStart, this);
22322 //        }
22323         
22324         if(this.autoslide){
22325             var _this = this;
22326             
22327             this.slideFn = window.setInterval(function() {
22328                 _this.showPanelNext();
22329             }, this.timer);
22330         }
22331         
22332         if(this.showarrow){
22333             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22334             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22335         }
22336         
22337         
22338     },
22339     
22340 //    onTouchStart : function(e, el, o)
22341 //    {
22342 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22343 //            return;
22344 //        }
22345 //        
22346 //        this.showPanelNext();
22347 //    },
22348     
22349     
22350     getChildContainer : function()
22351     {
22352         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22353     },
22354     
22355     /**
22356     * register a Navigation item
22357     * @param {Roo.bootstrap.nav.Item} the navitem to add
22358     */
22359     register : function(item)
22360     {
22361         this.tabs.push( item);
22362         item.navId = this.navId; // not really needed..
22363         this.addBullet();
22364     
22365     },
22366     
22367     getActivePanel : function()
22368     {
22369         var r = false;
22370         Roo.each(this.tabs, function(t) {
22371             if (t.active) {
22372                 r = t;
22373                 return false;
22374             }
22375             return null;
22376         });
22377         return r;
22378         
22379     },
22380     getPanelByName : function(n)
22381     {
22382         var r = false;
22383         Roo.each(this.tabs, function(t) {
22384             if (t.tabId == n) {
22385                 r = t;
22386                 return false;
22387             }
22388             return null;
22389         });
22390         return r;
22391     },
22392     indexOfPanel : function(p)
22393     {
22394         var r = false;
22395         Roo.each(this.tabs, function(t,i) {
22396             if (t.tabId == p.tabId) {
22397                 r = i;
22398                 return false;
22399             }
22400             return null;
22401         });
22402         return r;
22403     },
22404     /**
22405      * show a specific panel
22406      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22407      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22408      */
22409     showPanel : function (pan)
22410     {
22411         if(this.transition || typeof(pan) == 'undefined'){
22412             Roo.log("waiting for the transitionend");
22413             return false;
22414         }
22415         
22416         if (typeof(pan) == 'number') {
22417             pan = this.tabs[pan];
22418         }
22419         
22420         if (typeof(pan) == 'string') {
22421             pan = this.getPanelByName(pan);
22422         }
22423         
22424         var cur = this.getActivePanel();
22425         
22426         if(!pan || !cur){
22427             Roo.log('pan or acitve pan is undefined');
22428             return false;
22429         }
22430         
22431         if (pan.tabId == this.getActivePanel().tabId) {
22432             return true;
22433         }
22434         
22435         if (false === cur.fireEvent('beforedeactivate')) {
22436             return false;
22437         }
22438         
22439         if(this.bullets > 0 && !Roo.isTouch){
22440             this.setActiveBullet(this.indexOfPanel(pan));
22441         }
22442         
22443         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22444             
22445             //class="carousel-item carousel-item-next carousel-item-left"
22446             
22447             this.transition = true;
22448             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22449             var lr = dir == 'next' ? 'left' : 'right';
22450             pan.el.addClass(dir); // or prev
22451             pan.el.addClass('carousel-item-' + dir); // or prev
22452             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22453             cur.el.addClass(lr); // or right
22454             pan.el.addClass(lr);
22455             cur.el.addClass('carousel-item-' +lr); // or right
22456             pan.el.addClass('carousel-item-' +lr);
22457             
22458             
22459             var _this = this;
22460             cur.el.on('transitionend', function() {
22461                 Roo.log("trans end?");
22462                 
22463                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22464                 pan.setActive(true);
22465                 
22466                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22467                 cur.setActive(false);
22468                 
22469                 _this.transition = false;
22470                 
22471             }, this, { single:  true } );
22472             
22473             return true;
22474         }
22475         
22476         cur.setActive(false);
22477         pan.setActive(true);
22478         
22479         return true;
22480         
22481     },
22482     showPanelNext : function()
22483     {
22484         var i = this.indexOfPanel(this.getActivePanel());
22485         
22486         if (i >= this.tabs.length - 1 && !this.autoslide) {
22487             return;
22488         }
22489         
22490         if (i >= this.tabs.length - 1 && this.autoslide) {
22491             i = -1;
22492         }
22493         
22494         this.showPanel(this.tabs[i+1]);
22495     },
22496     
22497     showPanelPrev : function()
22498     {
22499         var i = this.indexOfPanel(this.getActivePanel());
22500         
22501         if (i  < 1 && !this.autoslide) {
22502             return;
22503         }
22504         
22505         if (i < 1 && this.autoslide) {
22506             i = this.tabs.length;
22507         }
22508         
22509         this.showPanel(this.tabs[i-1]);
22510     },
22511     
22512     
22513     addBullet: function()
22514     {
22515         if(!this.bullets || Roo.isTouch){
22516             return;
22517         }
22518         var ctr = this.el.select('.carousel-bullets',true).first();
22519         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22520         var bullet = ctr.createChild({
22521             cls : 'bullet bullet-' + i
22522         },ctr.dom.lastChild);
22523         
22524         
22525         var _this = this;
22526         
22527         bullet.on('click', (function(e, el, o, ii, t){
22528
22529             e.preventDefault();
22530
22531             this.showPanel(ii);
22532
22533             if(this.autoslide && this.slideFn){
22534                 clearInterval(this.slideFn);
22535                 this.slideFn = window.setInterval(function() {
22536                     _this.showPanelNext();
22537                 }, this.timer);
22538             }
22539
22540         }).createDelegate(this, [i, bullet], true));
22541                 
22542         
22543     },
22544      
22545     setActiveBullet : function(i)
22546     {
22547         if(Roo.isTouch){
22548             return;
22549         }
22550         
22551         Roo.each(this.el.select('.bullet', true).elements, function(el){
22552             el.removeClass('selected');
22553         });
22554
22555         var bullet = this.el.select('.bullet-' + i, true).first();
22556         
22557         if(!bullet){
22558             return;
22559         }
22560         
22561         bullet.addClass('selected');
22562     }
22563     
22564     
22565   
22566 });
22567
22568  
22569
22570  
22571  
22572 Roo.apply(Roo.bootstrap.TabGroup, {
22573     
22574     groups: {},
22575      /**
22576     * register a Navigation Group
22577     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22578     */
22579     register : function(navgrp)
22580     {
22581         this.groups[navgrp.navId] = navgrp;
22582         
22583     },
22584     /**
22585     * fetch a Navigation Group based on the navigation ID
22586     * if one does not exist , it will get created.
22587     * @param {string} the navgroup to add
22588     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22589     */
22590     get: function(navId) {
22591         if (typeof(this.groups[navId]) == 'undefined') {
22592             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22593         }
22594         return this.groups[navId] ;
22595     }
22596     
22597     
22598     
22599 });
22600
22601  /*
22602  * - LGPL
22603  *
22604  * TabPanel
22605  * 
22606  */
22607
22608 /**
22609  * @class Roo.bootstrap.TabPanel
22610  * @extends Roo.bootstrap.Component
22611  * @children Roo.bootstrap.Component
22612  * Bootstrap TabPanel class
22613  * @cfg {Boolean} active panel active
22614  * @cfg {String} html panel content
22615  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22616  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22617  * @cfg {String} href click to link..
22618  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22619  * 
22620  * 
22621  * @constructor
22622  * Create a new TabPanel
22623  * @param {Object} config The config object
22624  */
22625
22626 Roo.bootstrap.TabPanel = function(config){
22627     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22628     this.addEvents({
22629         /**
22630              * @event changed
22631              * Fires when the active status changes
22632              * @param {Roo.bootstrap.TabPanel} this
22633              * @param {Boolean} state the new state
22634             
22635          */
22636         'changed': true,
22637         /**
22638              * @event beforedeactivate
22639              * Fires before a tab is de-activated - can be used to do validation on a form.
22640              * @param {Roo.bootstrap.TabPanel} this
22641              * @return {Boolean} false if there is an error
22642             
22643          */
22644         'beforedeactivate': true
22645      });
22646     
22647     this.tabId = this.tabId || Roo.id();
22648   
22649 };
22650
22651 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22652     
22653     active: false,
22654     html: false,
22655     tabId: false,
22656     navId : false,
22657     href : '',
22658     touchSlide : false,
22659     getAutoCreate : function(){
22660         
22661         
22662         var cfg = {
22663             tag: 'div',
22664             // item is needed for carousel - not sure if it has any effect otherwise
22665             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22666             html: this.html || ''
22667         };
22668         
22669         if(this.active){
22670             cfg.cls += ' active';
22671         }
22672         
22673         if(this.tabId){
22674             cfg.tabId = this.tabId;
22675         }
22676         
22677         
22678         
22679         return cfg;
22680     },
22681     
22682     initEvents:  function()
22683     {
22684         var p = this.parent();
22685         
22686         this.navId = this.navId || p.navId;
22687         
22688         if (typeof(this.navId) != 'undefined') {
22689             // not really needed.. but just in case.. parent should be a NavGroup.
22690             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22691             
22692             tg.register(this);
22693             
22694             var i = tg.tabs.length - 1;
22695             
22696             if(this.active && tg.bullets > 0 && i < tg.bullets){
22697                 tg.setActiveBullet(i);
22698             }
22699         }
22700         
22701         this.el.on('click', this.onClick, this);
22702         
22703         if(Roo.isTouch && this.touchSlide){
22704             this.el.on("touchstart", this.onTouchStart, this);
22705             this.el.on("touchmove", this.onTouchMove, this);
22706             this.el.on("touchend", this.onTouchEnd, this);
22707         }
22708         
22709     },
22710     
22711     onRender : function(ct, position)
22712     {
22713         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22714     },
22715     
22716     setActive : function(state)
22717     {
22718         Roo.log("panel - set active " + this.tabId + "=" + state);
22719         
22720         this.active = state;
22721         if (!state) {
22722             this.el.removeClass('active');
22723             
22724         } else  if (!this.el.hasClass('active')) {
22725             this.el.addClass('active');
22726         }
22727         
22728         this.fireEvent('changed', this, state);
22729     },
22730     
22731     onClick : function(e)
22732     {
22733         e.preventDefault();
22734         
22735         if(!this.href.length){
22736             return;
22737         }
22738         
22739         window.location.href = this.href;
22740     },
22741     
22742     startX : 0,
22743     startY : 0,
22744     endX : 0,
22745     endY : 0,
22746     swiping : false,
22747     
22748     onTouchStart : function(e)
22749     {
22750         this.swiping = false;
22751         
22752         this.startX = e.browserEvent.touches[0].clientX;
22753         this.startY = e.browserEvent.touches[0].clientY;
22754     },
22755     
22756     onTouchMove : function(e)
22757     {
22758         this.swiping = true;
22759         
22760         this.endX = e.browserEvent.touches[0].clientX;
22761         this.endY = e.browserEvent.touches[0].clientY;
22762     },
22763     
22764     onTouchEnd : function(e)
22765     {
22766         if(!this.swiping){
22767             this.onClick(e);
22768             return;
22769         }
22770         
22771         var tabGroup = this.parent();
22772         
22773         if(this.endX > this.startX){ // swiping right
22774             tabGroup.showPanelPrev();
22775             return;
22776         }
22777         
22778         if(this.startX > this.endX){ // swiping left
22779             tabGroup.showPanelNext();
22780             return;
22781         }
22782     }
22783     
22784     
22785 });
22786  
22787
22788  
22789
22790  /*
22791  * - LGPL
22792  *
22793  * DateField
22794  * 
22795  */
22796
22797 /**
22798  * @class Roo.bootstrap.form.DateField
22799  * @extends Roo.bootstrap.form.Input
22800  * Bootstrap DateField class
22801  * @cfg {Number} weekStart default 0
22802  * @cfg {String} viewMode default empty, (months|years)
22803  * @cfg {String} minViewMode default empty, (months|years)
22804  * @cfg {Number} startDate default -Infinity
22805  * @cfg {Number} endDate default Infinity
22806  * @cfg {Boolean} todayHighlight default false
22807  * @cfg {Boolean} todayBtn default false
22808  * @cfg {Boolean} calendarWeeks default false
22809  * @cfg {Object} daysOfWeekDisabled default empty
22810  * @cfg {Boolean} singleMode default false (true | false)
22811  * 
22812  * @cfg {Boolean} keyboardNavigation default true
22813  * @cfg {String} language default en
22814  * 
22815  * @constructor
22816  * Create a new DateField
22817  * @param {Object} config The config object
22818  */
22819
22820 Roo.bootstrap.form.DateField = function(config){
22821     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22822      this.addEvents({
22823             /**
22824              * @event show
22825              * Fires when this field show.
22826              * @param {Roo.bootstrap.form.DateField} this
22827              * @param {Mixed} date The date value
22828              */
22829             show : true,
22830             /**
22831              * @event show
22832              * Fires when this field hide.
22833              * @param {Roo.bootstrap.form.DateField} this
22834              * @param {Mixed} date The date value
22835              */
22836             hide : true,
22837             /**
22838              * @event select
22839              * Fires when select a date.
22840              * @param {Roo.bootstrap.form.DateField} this
22841              * @param {Mixed} date The date value
22842              */
22843             select : true,
22844             /**
22845              * @event beforeselect
22846              * Fires when before select a date.
22847              * @param {Roo.bootstrap.form.DateField} this
22848              * @param {Mixed} date The date value
22849              */
22850             beforeselect : true
22851         });
22852 };
22853
22854 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22855     
22856     /**
22857      * @cfg {String} format
22858      * The default date format string which can be overriden for localization support.  The format must be
22859      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22860      */
22861     format : "m/d/y",
22862     /**
22863      * @cfg {String} altFormats
22864      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22865      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22866      */
22867     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22868     
22869     weekStart : 0,
22870     
22871     viewMode : '',
22872     
22873     minViewMode : '',
22874     
22875     todayHighlight : false,
22876     
22877     todayBtn: false,
22878     
22879     language: 'en',
22880     
22881     keyboardNavigation: true,
22882     
22883     calendarWeeks: false,
22884     
22885     startDate: -Infinity,
22886     
22887     endDate: Infinity,
22888     
22889     daysOfWeekDisabled: [],
22890     
22891     _events: [],
22892     
22893     singleMode : false,
22894     
22895     UTCDate: function()
22896     {
22897         return new Date(Date.UTC.apply(Date, arguments));
22898     },
22899     
22900     UTCToday: function()
22901     {
22902         var today = new Date();
22903         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22904     },
22905     
22906     getDate: function() {
22907             var d = this.getUTCDate();
22908             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22909     },
22910     
22911     getUTCDate: function() {
22912             return this.date;
22913     },
22914     
22915     setDate: function(d) {
22916             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22917     },
22918     
22919     setUTCDate: function(d) {
22920             this.date = d;
22921             this.setValue(this.formatDate(this.date));
22922     },
22923         
22924     onRender: function(ct, position)
22925     {
22926         
22927         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22928         
22929         this.language = this.language || 'en';
22930         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22932         
22933         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22934         this.format = this.format || 'm/d/y';
22935         this.isInline = false;
22936         this.isInput = true;
22937         this.component = this.el.select('.add-on', true).first() || false;
22938         this.component = (this.component && this.component.length === 0) ? false : this.component;
22939         this.hasInput = this.component && this.inputEl().length;
22940         
22941         if (typeof(this.minViewMode === 'string')) {
22942             switch (this.minViewMode) {
22943                 case 'months':
22944                     this.minViewMode = 1;
22945                     break;
22946                 case 'years':
22947                     this.minViewMode = 2;
22948                     break;
22949                 default:
22950                     this.minViewMode = 0;
22951                     break;
22952             }
22953         }
22954         
22955         if (typeof(this.viewMode === 'string')) {
22956             switch (this.viewMode) {
22957                 case 'months':
22958                     this.viewMode = 1;
22959                     break;
22960                 case 'years':
22961                     this.viewMode = 2;
22962                     break;
22963                 default:
22964                     this.viewMode = 0;
22965                     break;
22966             }
22967         }
22968                 
22969         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22970         
22971 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22972         
22973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22974         
22975         this.picker().on('mousedown', this.onMousedown, this);
22976         this.picker().on('click', this.onClick, this);
22977         
22978         this.picker().addClass('datepicker-dropdown');
22979         
22980         this.startViewMode = this.viewMode;
22981         
22982         if(this.singleMode){
22983             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22984                 v.setVisibilityMode(Roo.Element.DISPLAY);
22985                 v.hide();
22986             });
22987             
22988             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22989                 v.setStyle('width', '189px');
22990             });
22991         }
22992         
22993         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22994             if(!this.calendarWeeks){
22995                 v.remove();
22996                 return;
22997             }
22998             
22999             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23000             v.attr('colspan', function(i, val){
23001                 return parseInt(val) + 1;
23002             });
23003         });
23004                         
23005         
23006         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23007         
23008         this.setStartDate(this.startDate);
23009         this.setEndDate(this.endDate);
23010         
23011         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23012         
23013         this.fillDow();
23014         this.fillMonths();
23015         this.update();
23016         this.showMode();
23017         
23018         if(this.isInline) {
23019             this.showPopup();
23020         }
23021     },
23022     
23023     picker : function()
23024     {
23025         return this.pickerEl;
23026 //        return this.el.select('.datepicker', true).first();
23027     },
23028     
23029     fillDow: function()
23030     {
23031         var dowCnt = this.weekStart;
23032         
23033         var dow = {
23034             tag: 'tr',
23035             cn: [
23036                 
23037             ]
23038         };
23039         
23040         if(this.calendarWeeks){
23041             dow.cn.push({
23042                 tag: 'th',
23043                 cls: 'cw',
23044                 html: '&nbsp;'
23045             })
23046         }
23047         
23048         while (dowCnt < this.weekStart + 7) {
23049             dow.cn.push({
23050                 tag: 'th',
23051                 cls: 'dow',
23052                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23053             });
23054         }
23055         
23056         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23057     },
23058     
23059     fillMonths: function()
23060     {    
23061         var i = 0;
23062         var months = this.picker().select('>.datepicker-months td', true).first();
23063         
23064         months.dom.innerHTML = '';
23065         
23066         while (i < 12) {
23067             var month = {
23068                 tag: 'span',
23069                 cls: 'month',
23070                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23071             };
23072             
23073             months.createChild(month);
23074         }
23075         
23076     },
23077     
23078     update: function()
23079     {
23080         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;
23081         
23082         if (this.date < this.startDate) {
23083             this.viewDate = new Date(this.startDate);
23084         } else if (this.date > this.endDate) {
23085             this.viewDate = new Date(this.endDate);
23086         } else {
23087             this.viewDate = new Date(this.date);
23088         }
23089         
23090         this.fill();
23091     },
23092     
23093     fill: function() 
23094     {
23095         var d = new Date(this.viewDate),
23096                 year = d.getUTCFullYear(),
23097                 month = d.getUTCMonth(),
23098                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23099                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23100                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23101                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23102                 currentDate = this.date && this.date.valueOf(),
23103                 today = this.UTCToday();
23104         
23105         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23106         
23107 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23108         
23109 //        this.picker.select('>tfoot th.today').
23110 //                                              .text(dates[this.language].today)
23111 //                                              .toggle(this.todayBtn !== false);
23112     
23113         this.updateNavArrows();
23114         this.fillMonths();
23115                                                 
23116         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23117         
23118         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23119          
23120         prevMonth.setUTCDate(day);
23121         
23122         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23123         
23124         var nextMonth = new Date(prevMonth);
23125         
23126         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23127         
23128         nextMonth = nextMonth.valueOf();
23129         
23130         var fillMonths = false;
23131         
23132         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23133         
23134         while(prevMonth.valueOf() <= nextMonth) {
23135             var clsName = '';
23136             
23137             if (prevMonth.getUTCDay() === this.weekStart) {
23138                 if(fillMonths){
23139                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23140                 }
23141                     
23142                 fillMonths = {
23143                     tag: 'tr',
23144                     cn: []
23145                 };
23146                 
23147                 if(this.calendarWeeks){
23148                     // ISO 8601: First week contains first thursday.
23149                     // ISO also states week starts on Monday, but we can be more abstract here.
23150                     var
23151                     // Start of current week: based on weekstart/current date
23152                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23153                     // Thursday of this week
23154                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23155                     // First Thursday of year, year from thursday
23156                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23157                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23158                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23159                     
23160                     fillMonths.cn.push({
23161                         tag: 'td',
23162                         cls: 'cw',
23163                         html: calWeek
23164                     });
23165                 }
23166             }
23167             
23168             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23169                 clsName += ' old';
23170             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23171                 clsName += ' new';
23172             }
23173             if (this.todayHighlight &&
23174                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23175                 prevMonth.getUTCMonth() == today.getMonth() &&
23176                 prevMonth.getUTCDate() == today.getDate()) {
23177                 clsName += ' today';
23178             }
23179             
23180             if (currentDate && prevMonth.valueOf() === currentDate) {
23181                 clsName += ' active';
23182             }
23183             
23184             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23185                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23186                     clsName += ' disabled';
23187             }
23188             
23189             fillMonths.cn.push({
23190                 tag: 'td',
23191                 cls: 'day ' + clsName,
23192                 html: prevMonth.getDate()
23193             });
23194             
23195             prevMonth.setDate(prevMonth.getDate()+1);
23196         }
23197           
23198         var currentYear = this.date && this.date.getUTCFullYear();
23199         var currentMonth = this.date && this.date.getUTCMonth();
23200         
23201         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23202         
23203         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23204             v.removeClass('active');
23205             
23206             if(currentYear === year && k === currentMonth){
23207                 v.addClass('active');
23208             }
23209             
23210             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23211                 v.addClass('disabled');
23212             }
23213             
23214         });
23215         
23216         
23217         year = parseInt(year/10, 10) * 10;
23218         
23219         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23220         
23221         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23222         
23223         year -= 1;
23224         for (var i = -1; i < 11; i++) {
23225             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23226                 tag: 'span',
23227                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23228                 html: year
23229             });
23230             
23231             year += 1;
23232         }
23233     },
23234     
23235     showMode: function(dir) 
23236     {
23237         if (dir) {
23238             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23239         }
23240         
23241         Roo.each(this.picker().select('>div',true).elements, function(v){
23242             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23243             v.hide();
23244         });
23245         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23246     },
23247     
23248     place: function()
23249     {
23250         if(this.isInline) {
23251             return;
23252         }
23253         
23254         this.picker().removeClass(['bottom', 'top']);
23255         
23256         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23257             /*
23258              * place to the top of element!
23259              *
23260              */
23261             
23262             this.picker().addClass('top');
23263             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23264             
23265             return;
23266         }
23267         
23268         this.picker().addClass('bottom');
23269         
23270         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23271     },
23272     
23273     parseDate : function(value)
23274     {
23275         if(!value || value instanceof Date){
23276             return value;
23277         }
23278         var v = Date.parseDate(value, this.format);
23279         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23280             v = Date.parseDate(value, 'Y-m-d');
23281         }
23282         if(!v && this.altFormats){
23283             if(!this.altFormatsArray){
23284                 this.altFormatsArray = this.altFormats.split("|");
23285             }
23286             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23287                 v = Date.parseDate(value, this.altFormatsArray[i]);
23288             }
23289         }
23290         return v;
23291     },
23292     
23293     formatDate : function(date, fmt)
23294     {   
23295         return (!date || !(date instanceof Date)) ?
23296         date : date.dateFormat(fmt || this.format);
23297     },
23298     
23299     onFocus : function()
23300     {
23301         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23302         this.showPopup();
23303     },
23304     
23305     onBlur : function()
23306     {
23307         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23308         
23309         var d = this.inputEl().getValue();
23310         
23311         this.setValue(d);
23312                 
23313         this.hidePopup();
23314     },
23315     
23316     showPopup : function()
23317     {
23318         this.picker().show();
23319         this.update();
23320         this.place();
23321         
23322         this.fireEvent('showpopup', this, this.date);
23323     },
23324     
23325     hidePopup : function()
23326     {
23327         if(this.isInline) {
23328             return;
23329         }
23330         this.picker().hide();
23331         this.viewMode = this.startViewMode;
23332         this.showMode();
23333         
23334         this.fireEvent('hidepopup', this, this.date);
23335         
23336     },
23337     
23338     onMousedown: function(e)
23339     {
23340         e.stopPropagation();
23341         e.preventDefault();
23342     },
23343     
23344     keyup: function(e)
23345     {
23346         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23347         this.update();
23348     },
23349
23350     setValue: function(v)
23351     {
23352         if(this.fireEvent('beforeselect', this, v) !== false){
23353             var d = new Date(this.parseDate(v) ).clearTime();
23354         
23355             if(isNaN(d.getTime())){
23356                 this.date = this.viewDate = '';
23357                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23358                 return;
23359             }
23360
23361             v = this.formatDate(d);
23362
23363             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23364
23365             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23366
23367             this.update();
23368
23369             this.fireEvent('select', this, this.date);
23370         }
23371     },
23372     
23373     getValue: function()
23374     {
23375         return this.formatDate(this.date);
23376     },
23377     
23378     fireKey: function(e)
23379     {
23380         if (!this.picker().isVisible()){
23381             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23382                 this.showPopup();
23383             }
23384             return;
23385         }
23386         
23387         var dateChanged = false,
23388         dir, day, month,
23389         newDate, newViewDate;
23390         
23391         switch(e.keyCode){
23392             case 27: // escape
23393                 this.hidePopup();
23394                 e.preventDefault();
23395                 break;
23396             case 37: // left
23397             case 39: // right
23398                 if (!this.keyboardNavigation) {
23399                     break;
23400                 }
23401                 dir = e.keyCode == 37 ? -1 : 1;
23402                 
23403                 if (e.ctrlKey){
23404                     newDate = this.moveYear(this.date, dir);
23405                     newViewDate = this.moveYear(this.viewDate, dir);
23406                 } else if (e.shiftKey){
23407                     newDate = this.moveMonth(this.date, dir);
23408                     newViewDate = this.moveMonth(this.viewDate, dir);
23409                 } else {
23410                     newDate = new Date(this.date);
23411                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23412                     newViewDate = new Date(this.viewDate);
23413                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23414                 }
23415                 if (this.dateWithinRange(newDate)){
23416                     this.date = newDate;
23417                     this.viewDate = newViewDate;
23418                     this.setValue(this.formatDate(this.date));
23419 //                    this.update();
23420                     e.preventDefault();
23421                     dateChanged = true;
23422                 }
23423                 break;
23424             case 38: // up
23425             case 40: // down
23426                 if (!this.keyboardNavigation) {
23427                     break;
23428                 }
23429                 dir = e.keyCode == 38 ? -1 : 1;
23430                 if (e.ctrlKey){
23431                     newDate = this.moveYear(this.date, dir);
23432                     newViewDate = this.moveYear(this.viewDate, dir);
23433                 } else if (e.shiftKey){
23434                     newDate = this.moveMonth(this.date, dir);
23435                     newViewDate = this.moveMonth(this.viewDate, dir);
23436                 } else {
23437                     newDate = new Date(this.date);
23438                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23439                     newViewDate = new Date(this.viewDate);
23440                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23441                 }
23442                 if (this.dateWithinRange(newDate)){
23443                     this.date = newDate;
23444                     this.viewDate = newViewDate;
23445                     this.setValue(this.formatDate(this.date));
23446 //                    this.update();
23447                     e.preventDefault();
23448                     dateChanged = true;
23449                 }
23450                 break;
23451             case 13: // enter
23452                 this.setValue(this.formatDate(this.date));
23453                 this.hidePopup();
23454                 e.preventDefault();
23455                 break;
23456             case 9: // tab
23457                 this.setValue(this.formatDate(this.date));
23458                 this.hidePopup();
23459                 break;
23460             case 16: // shift
23461             case 17: // ctrl
23462             case 18: // alt
23463                 break;
23464             default :
23465                 this.hidePopup();
23466                 
23467         }
23468     },
23469     
23470     
23471     onClick: function(e) 
23472     {
23473         e.stopPropagation();
23474         e.preventDefault();
23475         
23476         var target = e.getTarget();
23477         
23478         if(target.nodeName.toLowerCase() === 'i'){
23479             target = Roo.get(target).dom.parentNode;
23480         }
23481         
23482         var nodeName = target.nodeName;
23483         var className = target.className;
23484         var html = target.innerHTML;
23485         //Roo.log(nodeName);
23486         
23487         switch(nodeName.toLowerCase()) {
23488             case 'th':
23489                 switch(className) {
23490                     case 'switch':
23491                         this.showMode(1);
23492                         break;
23493                     case 'prev':
23494                     case 'next':
23495                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23496                         switch(this.viewMode){
23497                                 case 0:
23498                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23499                                         break;
23500                                 case 1:
23501                                 case 2:
23502                                         this.viewDate = this.moveYear(this.viewDate, dir);
23503                                         break;
23504                         }
23505                         this.fill();
23506                         break;
23507                     case 'today':
23508                         var date = new Date();
23509                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23510 //                        this.fill()
23511                         this.setValue(this.formatDate(this.date));
23512                         
23513                         this.hidePopup();
23514                         break;
23515                 }
23516                 break;
23517             case 'span':
23518                 if (className.indexOf('disabled') < 0) {
23519                 if (!this.viewDate) {
23520                     this.viewDate = new Date();
23521                 }
23522                 this.viewDate.setUTCDate(1);
23523                     if (className.indexOf('month') > -1) {
23524                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23525                     } else {
23526                         var year = parseInt(html, 10) || 0;
23527                         this.viewDate.setUTCFullYear(year);
23528                         
23529                     }
23530                     
23531                     if(this.singleMode){
23532                         this.setValue(this.formatDate(this.viewDate));
23533                         this.hidePopup();
23534                         return;
23535                     }
23536                     
23537                     this.showMode(-1);
23538                     this.fill();
23539                 }
23540                 break;
23541                 
23542             case 'td':
23543                 //Roo.log(className);
23544                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23545                     var day = parseInt(html, 10) || 1;
23546                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23547                         month = (this.viewDate || new Date()).getUTCMonth();
23548
23549                     if (className.indexOf('old') > -1) {
23550                         if(month === 0 ){
23551                             month = 11;
23552                             year -= 1;
23553                         }else{
23554                             month -= 1;
23555                         }
23556                     } else if (className.indexOf('new') > -1) {
23557                         if (month == 11) {
23558                             month = 0;
23559                             year += 1;
23560                         } else {
23561                             month += 1;
23562                         }
23563                     }
23564                     //Roo.log([year,month,day]);
23565                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23566                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23567 //                    this.fill();
23568                     //Roo.log(this.formatDate(this.date));
23569                     this.setValue(this.formatDate(this.date));
23570                     this.hidePopup();
23571                 }
23572                 break;
23573         }
23574     },
23575     
23576     setStartDate: function(startDate)
23577     {
23578         this.startDate = startDate || -Infinity;
23579         if (this.startDate !== -Infinity) {
23580             this.startDate = this.parseDate(this.startDate);
23581         }
23582         this.update();
23583         this.updateNavArrows();
23584     },
23585
23586     setEndDate: function(endDate)
23587     {
23588         this.endDate = endDate || Infinity;
23589         if (this.endDate !== Infinity) {
23590             this.endDate = this.parseDate(this.endDate);
23591         }
23592         this.update();
23593         this.updateNavArrows();
23594     },
23595     
23596     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23597     {
23598         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23599         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23600             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23601         }
23602         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23603             return parseInt(d, 10);
23604         });
23605         this.update();
23606         this.updateNavArrows();
23607     },
23608     
23609     updateNavArrows: function() 
23610     {
23611         if(this.singleMode){
23612             return;
23613         }
23614         
23615         var d = new Date(this.viewDate),
23616         year = d.getUTCFullYear(),
23617         month = d.getUTCMonth();
23618         
23619         Roo.each(this.picker().select('.prev', true).elements, function(v){
23620             v.show();
23621             switch (this.viewMode) {
23622                 case 0:
23623
23624                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23625                         v.hide();
23626                     }
23627                     break;
23628                 case 1:
23629                 case 2:
23630                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23631                         v.hide();
23632                     }
23633                     break;
23634             }
23635         });
23636         
23637         Roo.each(this.picker().select('.next', true).elements, function(v){
23638             v.show();
23639             switch (this.viewMode) {
23640                 case 0:
23641
23642                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23643                         v.hide();
23644                     }
23645                     break;
23646                 case 1:
23647                 case 2:
23648                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23649                         v.hide();
23650                     }
23651                     break;
23652             }
23653         })
23654     },
23655     
23656     moveMonth: function(date, dir)
23657     {
23658         if (!dir) {
23659             return date;
23660         }
23661         var new_date = new Date(date.valueOf()),
23662         day = new_date.getUTCDate(),
23663         month = new_date.getUTCMonth(),
23664         mag = Math.abs(dir),
23665         new_month, test;
23666         dir = dir > 0 ? 1 : -1;
23667         if (mag == 1){
23668             test = dir == -1
23669             // If going back one month, make sure month is not current month
23670             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23671             ? function(){
23672                 return new_date.getUTCMonth() == month;
23673             }
23674             // If going forward one month, make sure month is as expected
23675             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23676             : function(){
23677                 return new_date.getUTCMonth() != new_month;
23678             };
23679             new_month = month + dir;
23680             new_date.setUTCMonth(new_month);
23681             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23682             if (new_month < 0 || new_month > 11) {
23683                 new_month = (new_month + 12) % 12;
23684             }
23685         } else {
23686             // For magnitudes >1, move one month at a time...
23687             for (var i=0; i<mag; i++) {
23688                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23689                 new_date = this.moveMonth(new_date, dir);
23690             }
23691             // ...then reset the day, keeping it in the new month
23692             new_month = new_date.getUTCMonth();
23693             new_date.setUTCDate(day);
23694             test = function(){
23695                 return new_month != new_date.getUTCMonth();
23696             };
23697         }
23698         // Common date-resetting loop -- if date is beyond end of month, make it
23699         // end of month
23700         while (test()){
23701             new_date.setUTCDate(--day);
23702             new_date.setUTCMonth(new_month);
23703         }
23704         return new_date;
23705     },
23706
23707     moveYear: function(date, dir)
23708     {
23709         return this.moveMonth(date, dir*12);
23710     },
23711
23712     dateWithinRange: function(date)
23713     {
23714         return date >= this.startDate && date <= this.endDate;
23715     },
23716
23717     
23718     remove: function() 
23719     {
23720         this.picker().remove();
23721     },
23722     
23723     validateValue : function(value)
23724     {
23725         if(this.getVisibilityEl().hasClass('hidden')){
23726             return true;
23727         }
23728         
23729         if(value.length < 1)  {
23730             if(this.allowBlank){
23731                 return true;
23732             }
23733             return false;
23734         }
23735         
23736         if(value.length < this.minLength){
23737             return false;
23738         }
23739         if(value.length > this.maxLength){
23740             return false;
23741         }
23742         if(this.vtype){
23743             var vt = Roo.form.VTypes;
23744             if(!vt[this.vtype](value, this)){
23745                 return false;
23746             }
23747         }
23748         if(typeof this.validator == "function"){
23749             var msg = this.validator(value);
23750             if(msg !== true){
23751                 return false;
23752             }
23753         }
23754         
23755         if(this.regex && !this.regex.test(value)){
23756             return false;
23757         }
23758         
23759         if(typeof(this.parseDate(value)) == 'undefined'){
23760             return false;
23761         }
23762         
23763         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23764             return false;
23765         }      
23766         
23767         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23768             return false;
23769         } 
23770         
23771         
23772         return true;
23773     },
23774     
23775     reset : function()
23776     {
23777         this.date = this.viewDate = '';
23778         
23779         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23780     }
23781    
23782 });
23783
23784 Roo.apply(Roo.bootstrap.form.DateField,  {
23785     
23786     head : {
23787         tag: 'thead',
23788         cn: [
23789         {
23790             tag: 'tr',
23791             cn: [
23792             {
23793                 tag: 'th',
23794                 cls: 'prev',
23795                 html: '<i class="fa fa-arrow-left"/>'
23796             },
23797             {
23798                 tag: 'th',
23799                 cls: 'switch',
23800                 colspan: '5'
23801             },
23802             {
23803                 tag: 'th',
23804                 cls: 'next',
23805                 html: '<i class="fa fa-arrow-right"/>'
23806             }
23807
23808             ]
23809         }
23810         ]
23811     },
23812     
23813     content : {
23814         tag: 'tbody',
23815         cn: [
23816         {
23817             tag: 'tr',
23818             cn: [
23819             {
23820                 tag: 'td',
23821                 colspan: '7'
23822             }
23823             ]
23824         }
23825         ]
23826     },
23827     
23828     footer : {
23829         tag: 'tfoot',
23830         cn: [
23831         {
23832             tag: 'tr',
23833             cn: [
23834             {
23835                 tag: 'th',
23836                 colspan: '7',
23837                 cls: 'today'
23838             }
23839                     
23840             ]
23841         }
23842         ]
23843     },
23844     
23845     dates:{
23846         en: {
23847             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23848             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23849             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23850             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23851             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23852             today: "Today"
23853         }
23854     },
23855     
23856     modes: [
23857     {
23858         clsName: 'days',
23859         navFnc: 'Month',
23860         navStep: 1
23861     },
23862     {
23863         clsName: 'months',
23864         navFnc: 'FullYear',
23865         navStep: 1
23866     },
23867     {
23868         clsName: 'years',
23869         navFnc: 'FullYear',
23870         navStep: 10
23871     }]
23872 });
23873
23874 Roo.apply(Roo.bootstrap.form.DateField,  {
23875   
23876     template : {
23877         tag: 'div',
23878         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23879         cn: [
23880         {
23881             tag: 'div',
23882             cls: 'datepicker-days',
23883             cn: [
23884             {
23885                 tag: 'table',
23886                 cls: 'table-condensed',
23887                 cn:[
23888                 Roo.bootstrap.form.DateField.head,
23889                 {
23890                     tag: 'tbody'
23891                 },
23892                 Roo.bootstrap.form.DateField.footer
23893                 ]
23894             }
23895             ]
23896         },
23897         {
23898             tag: 'div',
23899             cls: 'datepicker-months',
23900             cn: [
23901             {
23902                 tag: 'table',
23903                 cls: 'table-condensed',
23904                 cn:[
23905                 Roo.bootstrap.form.DateField.head,
23906                 Roo.bootstrap.form.DateField.content,
23907                 Roo.bootstrap.form.DateField.footer
23908                 ]
23909             }
23910             ]
23911         },
23912         {
23913             tag: 'div',
23914             cls: 'datepicker-years',
23915             cn: [
23916             {
23917                 tag: 'table',
23918                 cls: 'table-condensed',
23919                 cn:[
23920                 Roo.bootstrap.form.DateField.head,
23921                 Roo.bootstrap.form.DateField.content,
23922                 Roo.bootstrap.form.DateField.footer
23923                 ]
23924             }
23925             ]
23926         }
23927         ]
23928     }
23929 });
23930
23931  
23932
23933  /*
23934  * - LGPL
23935  *
23936  * TimeField
23937  * 
23938  */
23939
23940 /**
23941  * @class Roo.bootstrap.form.TimeField
23942  * @extends Roo.bootstrap.form.Input
23943  * Bootstrap DateField class
23944  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987     minuteStep : 1,
23988
23989     getAutoCreate : function()
23990     {
23991         this.after = '<i class="fa far fa-clock"></i>';
23992         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23993         
23994          
23995     },
23996     onRender: function(ct, position)
23997     {
23998         
23999         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24000                 
24001         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24002         
24003         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24004         
24005         this.pop = this.picker().select('>.datepicker-time',true).first();
24006         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24007         
24008         this.picker().on('mousedown', this.onMousedown, this);
24009         this.picker().on('click', this.onClick, this);
24010         
24011         this.picker().addClass('datepicker-dropdown');
24012     
24013         this.fillTime();
24014         this.update();
24015             
24016         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24017         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24018         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24019         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24020         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24021         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24022
24023     },
24024     
24025     fireKey: function(e){
24026         if (!this.picker().isVisible()){
24027             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24028                 this.show();
24029             }
24030             return;
24031         }
24032
24033         e.preventDefault();
24034         
24035         switch(e.keyCode){
24036             case 27: // escape
24037                 this.hide();
24038                 break;
24039             case 37: // left
24040             case 39: // right
24041                 this.onTogglePeriod();
24042                 break;
24043             case 38: // up
24044                 this.onIncrementMinutes();
24045                 break;
24046             case 40: // down
24047                 this.onDecrementMinutes();
24048                 break;
24049             case 13: // enter
24050             case 9: // tab
24051                 this.setTime();
24052                 break;
24053         }
24054     },
24055     
24056     onClick: function(e) {
24057         e.stopPropagation();
24058         e.preventDefault();
24059     },
24060     
24061     picker : function()
24062     {
24063         return this.pickerEl;
24064     },
24065     
24066     fillTime: function()
24067     {    
24068         var time = this.pop.select('tbody', true).first();
24069         
24070         time.dom.innerHTML = '';
24071         
24072         time.createChild({
24073             tag: 'tr',
24074             cn: [
24075                 {
24076                     tag: 'td',
24077                     cn: [
24078                         {
24079                             tag: 'a',
24080                             href: '#',
24081                             cls: 'btn',
24082                             cn: [
24083                                 {
24084                                     tag: 'i',
24085                                     cls: 'hours-up fa fas fa-chevron-up'
24086                                 }
24087                             ]
24088                         } 
24089                     ]
24090                 },
24091                 {
24092                     tag: 'td',
24093                     cls: 'separator'
24094                 },
24095                 {
24096                     tag: 'td',
24097                     cn: [
24098                         {
24099                             tag: 'a',
24100                             href: '#',
24101                             cls: 'btn',
24102                             cn: [
24103                                 {
24104                                     tag: 'i',
24105                                     cls: 'minutes-up fa fas fa-chevron-up'
24106                                 }
24107                             ]
24108                         }
24109                     ]
24110                 },
24111                 {
24112                     tag: 'td',
24113                     cls: 'separator'
24114                 }
24115             ]
24116         });
24117         
24118         time.createChild({
24119             tag: 'tr',
24120             cn: [
24121                 {
24122                     tag: 'td',
24123                     cn: [
24124                         {
24125                             tag: 'span',
24126                             cls: 'timepicker-hour',
24127                             html: '00'
24128                         }  
24129                     ]
24130                 },
24131                 {
24132                     tag: 'td',
24133                     cls: 'separator',
24134                     html: ':'
24135                 },
24136                 {
24137                     tag: 'td',
24138                     cn: [
24139                         {
24140                             tag: 'span',
24141                             cls: 'timepicker-minute',
24142                             html: '00'
24143                         }  
24144                     ]
24145                 },
24146                 {
24147                     tag: 'td',
24148                     cls: 'separator'
24149                 },
24150                 {
24151                     tag: 'td',
24152                     cn: [
24153                         {
24154                             tag: 'button',
24155                             type: 'button',
24156                             cls: 'btn btn-primary period',
24157                             html: 'AM'
24158                             
24159                         }
24160                     ]
24161                 }
24162             ]
24163         });
24164         
24165         time.createChild({
24166             tag: 'tr',
24167             cn: [
24168                 {
24169                     tag: 'td',
24170                     cn: [
24171                         {
24172                             tag: 'a',
24173                             href: '#',
24174                             cls: 'btn',
24175                             cn: [
24176                                 {
24177                                     tag: 'span',
24178                                     cls: 'hours-down fa fas fa-chevron-down'
24179                                 }
24180                             ]
24181                         }
24182                     ]
24183                 },
24184                 {
24185                     tag: 'td',
24186                     cls: 'separator'
24187                 },
24188                 {
24189                     tag: 'td',
24190                     cn: [
24191                         {
24192                             tag: 'a',
24193                             href: '#',
24194                             cls: 'btn',
24195                             cn: [
24196                                 {
24197                                     tag: 'span',
24198                                     cls: 'minutes-down fa fas fa-chevron-down'
24199                                 }
24200                             ]
24201                         }
24202                     ]
24203                 },
24204                 {
24205                     tag: 'td',
24206                     cls: 'separator'
24207                 }
24208             ]
24209         });
24210         
24211     },
24212     
24213     update: function()
24214     {
24215         // default minute is a multiple of minuteStep
24216         if(typeof(this.time) === 'undefined') {
24217             this.time = new Date();
24218             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24219         }
24220         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24221         
24222         this.fill();
24223     },
24224     
24225     fill: function() 
24226     {
24227         var hours = this.time.getHours();
24228         var minutes = this.time.getMinutes();
24229         var period = 'AM';
24230         
24231         if(hours > 11){
24232             period = 'PM';
24233         }
24234         
24235         if(hours == 0){
24236             hours = 12;
24237         }
24238         
24239         
24240         if(hours > 12){
24241             hours = hours - 12;
24242         }
24243         
24244         if(hours < 10){
24245             hours = '0' + hours;
24246         }
24247         
24248         if(minutes < 10){
24249             minutes = '0' + minutes;
24250         }
24251         
24252         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24253         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24254         this.pop.select('button', true).first().dom.innerHTML = period;
24255         
24256     },
24257     
24258     place: function()
24259     {   
24260         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24261         
24262         var cls = ['bottom'];
24263         
24264         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24265             cls.pop();
24266             cls.push('top');
24267         }
24268         
24269         cls.push('right');
24270         
24271         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24272             cls.pop();
24273             cls.push('left');
24274         }
24275         //this.picker().setXY(20000,20000);
24276         this.picker().addClass(cls.join('-'));
24277         
24278         var _this = this;
24279         
24280         Roo.each(cls, function(c){
24281             if(c == 'bottom'){
24282                 (function() {
24283                  //  
24284                 }).defer(200);
24285                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24286                 //_this.picker().setTop(_this.inputEl().getHeight());
24287                 return;
24288             }
24289             if(c == 'top'){
24290                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24291                 
24292                 //_this.picker().setTop(0 - _this.picker().getHeight());
24293                 return;
24294             }
24295             /*
24296             if(c == 'left'){
24297                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24298                 return;
24299             }
24300             if(c == 'right'){
24301                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24302                 return;
24303             }
24304             */
24305         });
24306         
24307     },
24308   
24309     onFocus : function()
24310     {
24311         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24312         this.show();
24313     },
24314     
24315     onBlur : function()
24316     {
24317         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24318         this.hide();
24319     },
24320     
24321     show : function()
24322     {
24323         this.picker().show();
24324         this.pop.show();
24325         this.update();
24326         this.place();
24327         
24328         this.fireEvent('show', this, this.date);
24329     },
24330     
24331     hide : function()
24332     {
24333         this.picker().hide();
24334         this.pop.hide();
24335         
24336         this.fireEvent('hide', this, this.date);
24337     },
24338     
24339     setTime : function()
24340     {
24341         this.hide();
24342         this.setValue(this.time.format(this.format));
24343         
24344         this.fireEvent('select', this, this.date);
24345         
24346         
24347     },
24348     
24349     onMousedown: function(e){
24350         e.stopPropagation();
24351         e.preventDefault();
24352     },
24353     
24354     onIncrementHours: function()
24355     {
24356         Roo.log('onIncrementHours');
24357         this.time = this.time.add(Date.HOUR, 1);
24358         this.update();
24359         
24360     },
24361     
24362     onDecrementHours: function()
24363     {
24364         Roo.log('onDecrementHours');
24365         this.time = this.time.add(Date.HOUR, -1);
24366         this.update();
24367     },
24368     
24369     onIncrementMinutes: function()
24370     {
24371         Roo.log('onIncrementMinutes');
24372         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24373         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24374         this.update();
24375     },
24376     
24377     onDecrementMinutes: function()
24378     {
24379         Roo.log('onDecrementMinutes');
24380         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24381         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24382         this.update();
24383     },
24384     
24385     onTogglePeriod: function()
24386     {
24387         Roo.log('onTogglePeriod');
24388         this.time = this.time.add(Date.HOUR, 12);
24389         this.update();
24390     }
24391     
24392    
24393 });
24394  
24395
24396 Roo.apply(Roo.bootstrap.form.TimeField,  {
24397   
24398     template : {
24399         tag: 'div',
24400         cls: 'datepicker dropdown-menu',
24401         cn: [
24402             {
24403                 tag: 'div',
24404                 cls: 'datepicker-time',
24405                 cn: [
24406                 {
24407                     tag: 'table',
24408                     cls: 'table-condensed',
24409                     cn:[
24410                         {
24411                             tag: 'tbody',
24412                             cn: [
24413                                 {
24414                                     tag: 'tr',
24415                                     cn: [
24416                                     {
24417                                         tag: 'td',
24418                                         colspan: '7'
24419                                     }
24420                                     ]
24421                                 }
24422                             ]
24423                         },
24424                         {
24425                             tag: 'tfoot',
24426                             cn: [
24427                                 {
24428                                     tag: 'tr',
24429                                     cn: [
24430                                     {
24431                                         tag: 'th',
24432                                         colspan: '7',
24433                                         cls: '',
24434                                         cn: [
24435                                             {
24436                                                 tag: 'button',
24437                                                 cls: 'btn btn-info ok',
24438                                                 html: 'OK'
24439                                             }
24440                                         ]
24441                                     }
24442                     
24443                                     ]
24444                                 }
24445                             ]
24446                         }
24447                     ]
24448                 }
24449                 ]
24450             }
24451         ]
24452     }
24453 });
24454
24455  
24456
24457  /*
24458  * - LGPL
24459  *
24460  * MonthField
24461  * 
24462  */
24463
24464 /**
24465  * @class Roo.bootstrap.form.MonthField
24466  * @extends Roo.bootstrap.form.Input
24467  * Bootstrap MonthField class
24468  * 
24469  * @cfg {String} language default en
24470  * 
24471  * @constructor
24472  * Create a new MonthField
24473  * @param {Object} config The config object
24474  */
24475
24476 Roo.bootstrap.form.MonthField = function(config){
24477     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24478     
24479     this.addEvents({
24480         /**
24481          * @event show
24482          * Fires when this field show.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         show : true,
24487         /**
24488          * @event show
24489          * Fires when this field hide.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {Mixed} date The date value
24492          */
24493         hide : true,
24494         /**
24495          * @event select
24496          * Fires when select a date.
24497          * @param {Roo.bootstrap.form.MonthField} this
24498          * @param {String} oldvalue The old value
24499          * @param {String} newvalue The new value
24500          */
24501         select : true
24502     });
24503 };
24504
24505 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24506     
24507     onRender: function(ct, position)
24508     {
24509         
24510         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24511         
24512         this.language = this.language || 'en';
24513         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24514         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24515         
24516         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24517         this.isInline = false;
24518         this.isInput = true;
24519         this.component = this.el.select('.add-on', true).first() || false;
24520         this.component = (this.component && this.component.length === 0) ? false : this.component;
24521         this.hasInput = this.component && this.inputEL().length;
24522         
24523         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24524         
24525         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24526         
24527         this.picker().on('mousedown', this.onMousedown, this);
24528         this.picker().on('click', this.onClick, this);
24529         
24530         this.picker().addClass('datepicker-dropdown');
24531         
24532         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24533             v.setStyle('width', '189px');
24534         });
24535         
24536         this.fillMonths();
24537         
24538         this.update();
24539         
24540         if(this.isInline) {
24541             this.show();
24542         }
24543         
24544     },
24545     
24546     setValue: function(v, suppressEvent)
24547     {   
24548         var o = this.getValue();
24549         
24550         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24551         
24552         this.update();
24553
24554         if(suppressEvent !== true){
24555             this.fireEvent('select', this, o, v);
24556         }
24557         
24558     },
24559     
24560     getValue: function()
24561     {
24562         return this.value;
24563     },
24564     
24565     onClick: function(e) 
24566     {
24567         e.stopPropagation();
24568         e.preventDefault();
24569         
24570         var target = e.getTarget();
24571         
24572         if(target.nodeName.toLowerCase() === 'i'){
24573             target = Roo.get(target).dom.parentNode;
24574         }
24575         
24576         var nodeName = target.nodeName;
24577         var className = target.className;
24578         var html = target.innerHTML;
24579         
24580         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24581             return;
24582         }
24583         
24584         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24585         
24586         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24587         
24588         this.hide();
24589                         
24590     },
24591     
24592     picker : function()
24593     {
24594         return this.pickerEl;
24595     },
24596     
24597     fillMonths: function()
24598     {    
24599         var i = 0;
24600         var months = this.picker().select('>.datepicker-months td', true).first();
24601         
24602         months.dom.innerHTML = '';
24603         
24604         while (i < 12) {
24605             var month = {
24606                 tag: 'span',
24607                 cls: 'month',
24608                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24609             };
24610             
24611             months.createChild(month);
24612         }
24613         
24614     },
24615     
24616     update: function()
24617     {
24618         var _this = this;
24619         
24620         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24621             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24622         }
24623         
24624         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24625             e.removeClass('active');
24626             
24627             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24628                 e.addClass('active');
24629             }
24630         })
24631     },
24632     
24633     place: function()
24634     {
24635         if(this.isInline) {
24636             return;
24637         }
24638         
24639         this.picker().removeClass(['bottom', 'top']);
24640         
24641         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24642             /*
24643              * place to the top of element!
24644              *
24645              */
24646             
24647             this.picker().addClass('top');
24648             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24649             
24650             return;
24651         }
24652         
24653         this.picker().addClass('bottom');
24654         
24655         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24656     },
24657     
24658     onFocus : function()
24659     {
24660         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24661         this.show();
24662     },
24663     
24664     onBlur : function()
24665     {
24666         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24667         
24668         var d = this.inputEl().getValue();
24669         
24670         this.setValue(d);
24671                 
24672         this.hide();
24673     },
24674     
24675     show : function()
24676     {
24677         this.picker().show();
24678         this.picker().select('>.datepicker-months', true).first().show();
24679         this.update();
24680         this.place();
24681         
24682         this.fireEvent('show', this, this.date);
24683     },
24684     
24685     hide : function()
24686     {
24687         if(this.isInline) {
24688             return;
24689         }
24690         this.picker().hide();
24691         this.fireEvent('hide', this, this.date);
24692         
24693     },
24694     
24695     onMousedown: function(e)
24696     {
24697         e.stopPropagation();
24698         e.preventDefault();
24699     },
24700     
24701     keyup: function(e)
24702     {
24703         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24704         this.update();
24705     },
24706
24707     fireKey: function(e)
24708     {
24709         if (!this.picker().isVisible()){
24710             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24711                 this.show();
24712             }
24713             return;
24714         }
24715         
24716         var dir;
24717         
24718         switch(e.keyCode){
24719             case 27: // escape
24720                 this.hide();
24721                 e.preventDefault();
24722                 break;
24723             case 37: // left
24724             case 39: // right
24725                 dir = e.keyCode == 37 ? -1 : 1;
24726                 
24727                 this.vIndex = this.vIndex + dir;
24728                 
24729                 if(this.vIndex < 0){
24730                     this.vIndex = 0;
24731                 }
24732                 
24733                 if(this.vIndex > 11){
24734                     this.vIndex = 11;
24735                 }
24736                 
24737                 if(isNaN(this.vIndex)){
24738                     this.vIndex = 0;
24739                 }
24740                 
24741                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24742                 
24743                 break;
24744             case 38: // up
24745             case 40: // down
24746                 
24747                 dir = e.keyCode == 38 ? -1 : 1;
24748                 
24749                 this.vIndex = this.vIndex + dir * 4;
24750                 
24751                 if(this.vIndex < 0){
24752                     this.vIndex = 0;
24753                 }
24754                 
24755                 if(this.vIndex > 11){
24756                     this.vIndex = 11;
24757                 }
24758                 
24759                 if(isNaN(this.vIndex)){
24760                     this.vIndex = 0;
24761                 }
24762                 
24763                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24764                 break;
24765                 
24766             case 13: // enter
24767                 
24768                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24769                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24770                 }
24771                 
24772                 this.hide();
24773                 e.preventDefault();
24774                 break;
24775             case 9: // tab
24776                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24777                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24778                 }
24779                 this.hide();
24780                 break;
24781             case 16: // shift
24782             case 17: // ctrl
24783             case 18: // alt
24784                 break;
24785             default :
24786                 this.hide();
24787                 
24788         }
24789     },
24790     
24791     remove: function() 
24792     {
24793         this.picker().remove();
24794     }
24795    
24796 });
24797
24798 Roo.apply(Roo.bootstrap.form.MonthField,  {
24799     
24800     content : {
24801         tag: 'tbody',
24802         cn: [
24803         {
24804             tag: 'tr',
24805             cn: [
24806             {
24807                 tag: 'td',
24808                 colspan: '7'
24809             }
24810             ]
24811         }
24812         ]
24813     },
24814     
24815     dates:{
24816         en: {
24817             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24818             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24819         }
24820     }
24821 });
24822
24823 Roo.apply(Roo.bootstrap.form.MonthField,  {
24824   
24825     template : {
24826         tag: 'div',
24827         cls: 'datepicker dropdown-menu roo-dynamic',
24828         cn: [
24829             {
24830                 tag: 'div',
24831                 cls: 'datepicker-months',
24832                 cn: [
24833                 {
24834                     tag: 'table',
24835                     cls: 'table-condensed',
24836                     cn:[
24837                         Roo.bootstrap.form.DateField.content
24838                     ]
24839                 }
24840                 ]
24841             }
24842         ]
24843     }
24844 });
24845
24846  
24847
24848  
24849  /*
24850  * - LGPL
24851  *
24852  * CheckBox
24853  * 
24854  */
24855
24856 /**
24857  * @class Roo.bootstrap.form.CheckBox
24858  * @extends Roo.bootstrap.form.Input
24859  * Bootstrap CheckBox class
24860  * 
24861  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24862  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24863  * @cfg {String} boxLabel The text that appears beside the checkbox
24864  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24865  * @cfg {Boolean} checked initnal the element
24866  * @cfg {Boolean} inline inline the element (default false)
24867  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24868  * @cfg {String} tooltip label tooltip
24869  * 
24870  * @constructor
24871  * Create a new CheckBox
24872  * @param {Object} config The config object
24873  */
24874
24875 Roo.bootstrap.form.CheckBox = function(config){
24876     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24877    
24878     this.addEvents({
24879         /**
24880         * @event check
24881         * Fires when the element is checked or unchecked.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         * @param {Boolean} checked The new checked value
24884         */
24885        check : true,
24886        /**
24887         * @event click
24888         * Fires when the element is click.
24889         * @param {Roo.bootstrap.form.CheckBox} this This input
24890         */
24891        click : true
24892     });
24893     
24894 };
24895
24896 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24897   
24898     inputType: 'checkbox',
24899     inputValue: 1,
24900     valueOff: 0,
24901     boxLabel: false,
24902     checked: false,
24903     weight : false,
24904     inline: false,
24905     tooltip : '',
24906     
24907     // checkbox success does not make any sense really.. 
24908     invalidClass : "",
24909     validClass : "",
24910     
24911     
24912     getAutoCreate : function()
24913     {
24914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24915         
24916         var id = Roo.id();
24917         
24918         var cfg = {};
24919         
24920         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24921         
24922         if(this.inline){
24923             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24924         }
24925         
24926         var input =  {
24927             tag: 'input',
24928             id : id,
24929             type : this.inputType,
24930             value : this.inputValue,
24931             cls : 'roo-' + this.inputType, //'form-box',
24932             placeholder : this.placeholder || ''
24933             
24934         };
24935         
24936         if(this.inputType != 'radio'){
24937             var hidden =  {
24938                 tag: 'input',
24939                 type : 'hidden',
24940                 cls : 'roo-hidden-value',
24941                 value : this.checked ? this.inputValue : this.valueOff
24942             };
24943         }
24944         
24945             
24946         if (this.weight) { // Validity check?
24947             cfg.cls += " " + this.inputType + "-" + this.weight;
24948         }
24949         
24950         if (this.disabled) {
24951             input.disabled=true;
24952         }
24953         
24954         if(this.checked){
24955             input.checked = this.checked;
24956         }
24957         
24958         if (this.name) {
24959             
24960             input.name = this.name;
24961             
24962             if(this.inputType != 'radio'){
24963                 hidden.name = this.name;
24964                 input.name = '_hidden_' + this.name;
24965             }
24966         }
24967         
24968         if (this.size) {
24969             input.cls += ' input-' + this.size;
24970         }
24971         
24972         var settings=this;
24973         
24974         ['xs','sm','md','lg'].map(function(size){
24975             if (settings[size]) {
24976                 cfg.cls += ' col-' + size + '-' + settings[size];
24977             }
24978         });
24979         
24980         var inputblock = input;
24981          
24982         if (this.before || this.after) {
24983             
24984             inputblock = {
24985                 cls : 'input-group',
24986                 cn :  [] 
24987             };
24988             
24989             if (this.before) {
24990                 inputblock.cn.push({
24991                     tag :'span',
24992                     cls : 'input-group-addon',
24993                     html : this.before
24994                 });
24995             }
24996             
24997             inputblock.cn.push(input);
24998             
24999             if(this.inputType != 'radio'){
25000                 inputblock.cn.push(hidden);
25001             }
25002             
25003             if (this.after) {
25004                 inputblock.cn.push({
25005                     tag :'span',
25006                     cls : 'input-group-addon',
25007                     html : this.after
25008                 });
25009             }
25010             
25011         }
25012         var boxLabelCfg = false;
25013         
25014         if(this.boxLabel){
25015            
25016             boxLabelCfg = {
25017                 tag: 'label',
25018                 //'for': id, // box label is handled by onclick - so no for...
25019                 cls: 'box-label',
25020                 html: this.boxLabel
25021             };
25022             if(this.tooltip){
25023                 boxLabelCfg.tooltip = this.tooltip;
25024             }
25025              
25026         }
25027         
25028         
25029         if (align ==='left' && this.fieldLabel.length) {
25030 //                Roo.log("left and has label");
25031             cfg.cn = [
25032                 {
25033                     tag: 'label',
25034                     'for' :  id,
25035                     cls : 'control-label',
25036                     html : this.fieldLabel
25037                 },
25038                 {
25039                     cls : "", 
25040                     cn: [
25041                         inputblock
25042                     ]
25043                 }
25044             ];
25045             
25046             if (boxLabelCfg) {
25047                 cfg.cn[1].cn.push(boxLabelCfg);
25048             }
25049             
25050             if(this.labelWidth > 12){
25051                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25052             }
25053             
25054             if(this.labelWidth < 13 && this.labelmd == 0){
25055                 this.labelmd = this.labelWidth;
25056             }
25057             
25058             if(this.labellg > 0){
25059                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25060                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25061             }
25062             
25063             if(this.labelmd > 0){
25064                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25065                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25066             }
25067             
25068             if(this.labelsm > 0){
25069                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25070                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25071             }
25072             
25073             if(this.labelxs > 0){
25074                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25075                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25076             }
25077             
25078         } else if ( this.fieldLabel.length) {
25079 //                Roo.log(" label");
25080                 cfg.cn = [
25081                    
25082                     {
25083                         tag: this.boxLabel ? 'span' : 'label',
25084                         'for': id,
25085                         cls: 'control-label box-input-label',
25086                         //cls : 'input-group-addon',
25087                         html : this.fieldLabel
25088                     },
25089                     
25090                     inputblock
25091                     
25092                 ];
25093                 if (boxLabelCfg) {
25094                     cfg.cn.push(boxLabelCfg);
25095                 }
25096
25097         } else {
25098             
25099 //                Roo.log(" no label && no align");
25100                 cfg.cn = [  inputblock ] ;
25101                 if (boxLabelCfg) {
25102                     cfg.cn.push(boxLabelCfg);
25103                 }
25104
25105                 
25106         }
25107         
25108        
25109         
25110         if(this.inputType != 'radio'){
25111             cfg.cn.push(hidden);
25112         }
25113         
25114         return cfg;
25115         
25116     },
25117     
25118     /**
25119      * return the real input element.
25120      */
25121     inputEl: function ()
25122     {
25123         return this.el.select('input.roo-' + this.inputType,true).first();
25124     },
25125     hiddenEl: function ()
25126     {
25127         return this.el.select('input.roo-hidden-value',true).first();
25128     },
25129     
25130     labelEl: function()
25131     {
25132         return this.el.select('label.control-label',true).first();
25133     },
25134     /* depricated... */
25135     
25136     label: function()
25137     {
25138         return this.labelEl();
25139     },
25140     
25141     boxLabelEl: function()
25142     {
25143         return this.el.select('label.box-label',true).first();
25144     },
25145     
25146     initEvents : function()
25147     {
25148 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25149         
25150         this.inputEl().on('click', this.onClick,  this);
25151         
25152         if (this.boxLabel) { 
25153             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25154         }
25155         
25156         this.startValue = this.getValue();
25157         
25158         if(this.groupId){
25159             Roo.bootstrap.form.CheckBox.register(this);
25160         }
25161     },
25162     
25163     onClick : function(e)
25164     {   
25165         if(this.fireEvent('click', this, e) !== false){
25166             this.setChecked(!this.checked);
25167         }
25168         
25169     },
25170     
25171     setChecked : function(state,suppressEvent)
25172     {
25173         this.startValue = this.getValue();
25174
25175         if(this.inputType == 'radio'){
25176             
25177             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25178                 e.dom.checked = false;
25179             });
25180             
25181             this.inputEl().dom.checked = true;
25182             
25183             this.inputEl().dom.value = this.inputValue;
25184             
25185             if(suppressEvent !== true){
25186                 this.fireEvent('check', this, true);
25187             }
25188             
25189             this.validate();
25190             
25191             return;
25192         }
25193         
25194         this.checked = state;
25195         
25196         this.inputEl().dom.checked = state;
25197         
25198         
25199         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25200         
25201         if(suppressEvent !== true){
25202             this.fireEvent('check', this, state);
25203         }
25204         
25205         this.validate();
25206     },
25207     
25208     getValue : function()
25209     {
25210         if(this.inputType == 'radio'){
25211             return this.getGroupValue();
25212         }
25213         
25214         return this.hiddenEl().dom.value;
25215         
25216     },
25217     
25218     getGroupValue : function()
25219     {
25220         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25221             return '';
25222         }
25223         
25224         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25225     },
25226     
25227     setValue : function(v,suppressEvent)
25228     {
25229         if(this.inputType == 'radio'){
25230             this.setGroupValue(v, suppressEvent);
25231             return;
25232         }
25233         
25234         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25235         
25236         this.validate();
25237     },
25238     
25239     setGroupValue : function(v, suppressEvent)
25240     {
25241         this.startValue = this.getValue();
25242         
25243         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25244             e.dom.checked = false;
25245             
25246             if(e.dom.value == v){
25247                 e.dom.checked = true;
25248             }
25249         });
25250         
25251         if(suppressEvent !== true){
25252             this.fireEvent('check', this, true);
25253         }
25254
25255         this.validate();
25256         
25257         return;
25258     },
25259     
25260     validate : function()
25261     {
25262         if(this.getVisibilityEl().hasClass('hidden')){
25263             return true;
25264         }
25265         
25266         if(
25267                 this.disabled || 
25268                 (this.inputType == 'radio' && this.validateRadio()) ||
25269                 (this.inputType == 'checkbox' && this.validateCheckbox())
25270         ){
25271             this.markValid();
25272             return true;
25273         }
25274         
25275         this.markInvalid();
25276         return false;
25277     },
25278     
25279     validateRadio : function()
25280     {
25281         if(this.getVisibilityEl().hasClass('hidden')){
25282             return true;
25283         }
25284         
25285         if(this.allowBlank){
25286             return true;
25287         }
25288         
25289         var valid = false;
25290         
25291         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25292             if(!e.dom.checked){
25293                 return;
25294             }
25295             
25296             valid = true;
25297             
25298             return false;
25299         });
25300         
25301         return valid;
25302     },
25303     
25304     validateCheckbox : function()
25305     {
25306         if(!this.groupId){
25307             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25308             //return (this.getValue() == this.inputValue) ? true : false;
25309         }
25310         
25311         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25312         
25313         if(!group){
25314             return false;
25315         }
25316         
25317         var r = false;
25318         
25319         for(var i in group){
25320             if(group[i].el.isVisible(true)){
25321                 r = false;
25322                 break;
25323             }
25324             
25325             r = true;
25326         }
25327         
25328         for(var i in group){
25329             if(r){
25330                 break;
25331             }
25332             
25333             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25334         }
25335         
25336         return r;
25337     },
25338     
25339     /**
25340      * Mark this field as valid
25341      */
25342     markValid : function()
25343     {
25344         var _this = this;
25345         
25346         this.fireEvent('valid', this);
25347         
25348         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25349         
25350         if(this.groupId){
25351             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25352         }
25353         
25354         if(label){
25355             label.markValid();
25356         }
25357
25358         if(this.inputType == 'radio'){
25359             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25360                 var fg = e.findParent('.form-group', false, true);
25361                 if (Roo.bootstrap.version == 3) {
25362                     fg.removeClass([_this.invalidClass, _this.validClass]);
25363                     fg.addClass(_this.validClass);
25364                 } else {
25365                     fg.removeClass(['is-valid', 'is-invalid']);
25366                     fg.addClass('is-valid');
25367                 }
25368             });
25369             
25370             return;
25371         }
25372
25373         if(!this.groupId){
25374             var fg = this.el.findParent('.form-group', false, true);
25375             if (Roo.bootstrap.version == 3) {
25376                 fg.removeClass([this.invalidClass, this.validClass]);
25377                 fg.addClass(this.validClass);
25378             } else {
25379                 fg.removeClass(['is-valid', 'is-invalid']);
25380                 fg.addClass('is-valid');
25381             }
25382             return;
25383         }
25384         
25385         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25386         
25387         if(!group){
25388             return;
25389         }
25390         
25391         for(var i in group){
25392             var fg = group[i].el.findParent('.form-group', false, true);
25393             if (Roo.bootstrap.version == 3) {
25394                 fg.removeClass([this.invalidClass, this.validClass]);
25395                 fg.addClass(this.validClass);
25396             } else {
25397                 fg.removeClass(['is-valid', 'is-invalid']);
25398                 fg.addClass('is-valid');
25399             }
25400         }
25401     },
25402     
25403      /**
25404      * Mark this field as invalid
25405      * @param {String} msg The validation message
25406      */
25407     markInvalid : function(msg)
25408     {
25409         if(this.allowBlank){
25410             return;
25411         }
25412         
25413         var _this = this;
25414         
25415         this.fireEvent('invalid', this, msg);
25416         
25417         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25418         
25419         if(this.groupId){
25420             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25421         }
25422         
25423         if(label){
25424             label.markInvalid();
25425         }
25426             
25427         if(this.inputType == 'radio'){
25428             
25429             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25430                 var fg = e.findParent('.form-group', false, true);
25431                 if (Roo.bootstrap.version == 3) {
25432                     fg.removeClass([_this.invalidClass, _this.validClass]);
25433                     fg.addClass(_this.invalidClass);
25434                 } else {
25435                     fg.removeClass(['is-invalid', 'is-valid']);
25436                     fg.addClass('is-invalid');
25437                 }
25438             });
25439             
25440             return;
25441         }
25442         
25443         if(!this.groupId){
25444             var fg = this.el.findParent('.form-group', false, true);
25445             if (Roo.bootstrap.version == 3) {
25446                 fg.removeClass([_this.invalidClass, _this.validClass]);
25447                 fg.addClass(_this.invalidClass);
25448             } else {
25449                 fg.removeClass(['is-invalid', 'is-valid']);
25450                 fg.addClass('is-invalid');
25451             }
25452             return;
25453         }
25454         
25455         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25456         
25457         if(!group){
25458             return;
25459         }
25460         
25461         for(var i in group){
25462             var fg = group[i].el.findParent('.form-group', false, true);
25463             if (Roo.bootstrap.version == 3) {
25464                 fg.removeClass([_this.invalidClass, _this.validClass]);
25465                 fg.addClass(_this.invalidClass);
25466             } else {
25467                 fg.removeClass(['is-invalid', 'is-valid']);
25468                 fg.addClass('is-invalid');
25469             }
25470         }
25471         
25472     },
25473     
25474     clearInvalid : function()
25475     {
25476         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25477         
25478         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25479         
25480         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25481         
25482         if (label && label.iconEl) {
25483             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25484             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25485         }
25486     },
25487     
25488     disable : function()
25489     {
25490         if(this.inputType != 'radio'){
25491             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25492             return;
25493         }
25494         
25495         var _this = this;
25496         
25497         if(this.rendered){
25498             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25499                 _this.getActionEl().addClass(this.disabledClass);
25500                 e.dom.disabled = true;
25501             });
25502         }
25503         
25504         this.disabled = true;
25505         this.fireEvent("disable", this);
25506         return this;
25507     },
25508
25509     enable : function()
25510     {
25511         if(this.inputType != 'radio'){
25512             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25513             return;
25514         }
25515         
25516         var _this = this;
25517         
25518         if(this.rendered){
25519             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25520                 _this.getActionEl().removeClass(this.disabledClass);
25521                 e.dom.disabled = false;
25522             });
25523         }
25524         
25525         this.disabled = false;
25526         this.fireEvent("enable", this);
25527         return this;
25528     },
25529     
25530     setBoxLabel : function(v)
25531     {
25532         this.boxLabel = v;
25533         
25534         if(this.rendered){
25535             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25536         }
25537     }
25538
25539 });
25540
25541 Roo.apply(Roo.bootstrap.form.CheckBox, {
25542     
25543     groups: {},
25544     
25545      /**
25546     * register a CheckBox Group
25547     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25548     */
25549     register : function(checkbox)
25550     {
25551         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25552             this.groups[checkbox.groupId] = {};
25553         }
25554         
25555         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25556             return;
25557         }
25558         
25559         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25560         
25561     },
25562     /**
25563     * fetch a CheckBox Group based on the group ID
25564     * @param {string} the group ID
25565     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25566     */
25567     get: function(groupId) {
25568         if (typeof(this.groups[groupId]) == 'undefined') {
25569             return false;
25570         }
25571         
25572         return this.groups[groupId] ;
25573     }
25574     
25575     
25576 });
25577 /*
25578  * - LGPL
25579  *
25580  * RadioItem
25581  * 
25582  */
25583
25584 /**
25585  * @class Roo.bootstrap.form.Radio
25586  * @extends Roo.bootstrap.Component
25587  * Bootstrap Radio class
25588  * @cfg {String} boxLabel - the label associated
25589  * @cfg {String} value - the value of radio
25590  * 
25591  * @constructor
25592  * Create a new Radio
25593  * @param {Object} config The config object
25594  */
25595 Roo.bootstrap.form.Radio = function(config){
25596     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25597     
25598 };
25599
25600 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25601     
25602     boxLabel : '',
25603     
25604     value : '',
25605     
25606     getAutoCreate : function()
25607     {
25608         var cfg = {
25609             tag : 'div',
25610             cls : 'form-group radio',
25611             cn : [
25612                 {
25613                     tag : 'label',
25614                     cls : 'box-label',
25615                     html : this.boxLabel
25616                 }
25617             ]
25618         };
25619         
25620         return cfg;
25621     },
25622     
25623     initEvents : function() 
25624     {
25625         this.parent().register(this);
25626         
25627         this.el.on('click', this.onClick, this);
25628         
25629     },
25630     
25631     onClick : function(e)
25632     {
25633         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25634             this.setChecked(true);
25635         }
25636     },
25637     
25638     setChecked : function(state, suppressEvent)
25639     {
25640         this.parent().setValue(this.value, suppressEvent);
25641         
25642     },
25643     
25644     setBoxLabel : function(v)
25645     {
25646         this.boxLabel = v;
25647         
25648         if(this.rendered){
25649             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25650         }
25651     }
25652     
25653 });
25654  
25655
25656  /*
25657  * - LGPL
25658  *
25659  * Input
25660  * 
25661  */
25662
25663 /**
25664  * @class Roo.bootstrap.form.SecurePass
25665  * @extends Roo.bootstrap.form.Input
25666  * Bootstrap SecurePass class
25667  *
25668  * 
25669  * @constructor
25670  * Create a new SecurePass
25671  * @param {Object} config The config object
25672  */
25673  
25674 Roo.bootstrap.form.SecurePass = function (config) {
25675     // these go here, so the translation tool can replace them..
25676     this.errors = {
25677         PwdEmpty: "Please type a password, and then retype it to confirm.",
25678         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25679         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25680         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25681         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25682         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25683         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25684         TooWeak: "Your password is Too Weak."
25685     },
25686     this.meterLabel = "Password strength:";
25687     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25688     this.meterClass = [
25689         "roo-password-meter-tooweak", 
25690         "roo-password-meter-weak", 
25691         "roo-password-meter-medium", 
25692         "roo-password-meter-strong", 
25693         "roo-password-meter-grey"
25694     ];
25695     
25696     this.errors = {};
25697     
25698     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25699 }
25700
25701 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25702     /**
25703      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25704      * {
25705      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25706      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25707      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25708      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25709      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25710      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25711      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25712      * })
25713      */
25714     // private
25715     
25716     meterWidth: 300,
25717     errorMsg :'',    
25718     errors: false,
25719     imageRoot: '/',
25720     /**
25721      * @cfg {String/Object} Label for the strength meter (defaults to
25722      * 'Password strength:')
25723      */
25724     // private
25725     meterLabel: '',
25726     /**
25727      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25728      * ['Weak', 'Medium', 'Strong'])
25729      */
25730     // private    
25731     pwdStrengths: false,    
25732     // private
25733     strength: 0,
25734     // private
25735     _lastPwd: null,
25736     // private
25737     kCapitalLetter: 0,
25738     kSmallLetter: 1,
25739     kDigit: 2,
25740     kPunctuation: 3,
25741     
25742     insecure: false,
25743     // private
25744     initEvents: function ()
25745     {
25746         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25747
25748         if (this.el.is('input[type=password]') && Roo.isSafari) {
25749             this.el.on('keydown', this.SafariOnKeyDown, this);
25750         }
25751
25752         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25753     },
25754     // private
25755     onRender: function (ct, position)
25756     {
25757         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25758         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25759         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25760
25761         this.trigger.createChild({
25762                    cn: [
25763                     {
25764                     //id: 'PwdMeter',
25765                     tag: 'div',
25766                     cls: 'roo-password-meter-grey col-xs-12',
25767                     style: {
25768                         //width: 0,
25769                         //width: this.meterWidth + 'px'                                                
25770                         }
25771                     },
25772                     {                            
25773                          cls: 'roo-password-meter-text'                          
25774                     }
25775                 ]            
25776         });
25777
25778          
25779         if (this.hideTrigger) {
25780             this.trigger.setDisplayed(false);
25781         }
25782         this.setSize(this.width || '', this.height || '');
25783     },
25784     // private
25785     onDestroy: function ()
25786     {
25787         if (this.trigger) {
25788             this.trigger.removeAllListeners();
25789             this.trigger.remove();
25790         }
25791         if (this.wrap) {
25792             this.wrap.remove();
25793         }
25794         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25795     },
25796     // private
25797     checkStrength: function ()
25798     {
25799         var pwd = this.inputEl().getValue();
25800         if (pwd == this._lastPwd) {
25801             return;
25802         }
25803
25804         var strength;
25805         if (this.ClientSideStrongPassword(pwd)) {
25806             strength = 3;
25807         } else if (this.ClientSideMediumPassword(pwd)) {
25808             strength = 2;
25809         } else if (this.ClientSideWeakPassword(pwd)) {
25810             strength = 1;
25811         } else {
25812             strength = 0;
25813         }
25814         
25815         Roo.log('strength1: ' + strength);
25816         
25817         //var pm = this.trigger.child('div/div/div').dom;
25818         var pm = this.trigger.child('div/div');
25819         pm.removeClass(this.meterClass);
25820         pm.addClass(this.meterClass[strength]);
25821                 
25822         
25823         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25824                 
25825         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25826         
25827         this._lastPwd = pwd;
25828     },
25829     reset: function ()
25830     {
25831         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25832         
25833         this._lastPwd = '';
25834         
25835         var pm = this.trigger.child('div/div');
25836         pm.removeClass(this.meterClass);
25837         pm.addClass('roo-password-meter-grey');        
25838         
25839         
25840         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25841         
25842         pt.innerHTML = '';
25843         this.inputEl().dom.type='password';
25844     },
25845     // private
25846     validateValue: function (value)
25847     {
25848         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25849             return false;
25850         }
25851         if (value.length == 0) {
25852             if (this.allowBlank) {
25853                 this.clearInvalid();
25854                 return true;
25855             }
25856
25857             this.markInvalid(this.errors.PwdEmpty);
25858             this.errorMsg = this.errors.PwdEmpty;
25859             return false;
25860         }
25861         
25862         if(this.insecure){
25863             return true;
25864         }
25865         
25866         if (!value.match(/[\x21-\x7e]+/)) {
25867             this.markInvalid(this.errors.PwdBadChar);
25868             this.errorMsg = this.errors.PwdBadChar;
25869             return false;
25870         }
25871         if (value.length < 6) {
25872             this.markInvalid(this.errors.PwdShort);
25873             this.errorMsg = this.errors.PwdShort;
25874             return false;
25875         }
25876         if (value.length > 16) {
25877             this.markInvalid(this.errors.PwdLong);
25878             this.errorMsg = this.errors.PwdLong;
25879             return false;
25880         }
25881         var strength;
25882         if (this.ClientSideStrongPassword(value)) {
25883             strength = 3;
25884         } else if (this.ClientSideMediumPassword(value)) {
25885             strength = 2;
25886         } else if (this.ClientSideWeakPassword(value)) {
25887             strength = 1;
25888         } else {
25889             strength = 0;
25890         }
25891
25892         
25893         if (strength < 2) {
25894             //this.markInvalid(this.errors.TooWeak);
25895             this.errorMsg = this.errors.TooWeak;
25896             //return false;
25897         }
25898         
25899         
25900         console.log('strength2: ' + strength);
25901         
25902         //var pm = this.trigger.child('div/div/div').dom;
25903         
25904         var pm = this.trigger.child('div/div');
25905         pm.removeClass(this.meterClass);
25906         pm.addClass(this.meterClass[strength]);
25907                 
25908         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25909                 
25910         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25911         
25912         this.errorMsg = ''; 
25913         return true;
25914     },
25915     // private
25916     CharacterSetChecks: function (type)
25917     {
25918         this.type = type;
25919         this.fResult = false;
25920     },
25921     // private
25922     isctype: function (character, type)
25923     {
25924         switch (type) {  
25925             case this.kCapitalLetter:
25926                 if (character >= 'A' && character <= 'Z') {
25927                     return true;
25928                 }
25929                 break;
25930             
25931             case this.kSmallLetter:
25932                 if (character >= 'a' && character <= 'z') {
25933                     return true;
25934                 }
25935                 break;
25936             
25937             case this.kDigit:
25938                 if (character >= '0' && character <= '9') {
25939                     return true;
25940                 }
25941                 break;
25942             
25943             case this.kPunctuation:
25944                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25945                     return true;
25946                 }
25947                 break;
25948             
25949             default:
25950                 return false;
25951         }
25952
25953     },
25954     // private
25955     IsLongEnough: function (pwd, size)
25956     {
25957         return !(pwd == null || isNaN(size) || pwd.length < size);
25958     },
25959     // private
25960     SpansEnoughCharacterSets: function (word, nb)
25961     {
25962         if (!this.IsLongEnough(word, nb))
25963         {
25964             return false;
25965         }
25966
25967         var characterSetChecks = new Array(
25968             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25969             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25970         );
25971         
25972         for (var index = 0; index < word.length; ++index) {
25973             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25974                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25975                     characterSetChecks[nCharSet].fResult = true;
25976                     break;
25977                 }
25978             }
25979         }
25980
25981         var nCharSets = 0;
25982         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25983             if (characterSetChecks[nCharSet].fResult) {
25984                 ++nCharSets;
25985             }
25986         }
25987
25988         if (nCharSets < nb) {
25989             return false;
25990         }
25991         return true;
25992     },
25993     // private
25994     ClientSideStrongPassword: function (pwd)
25995     {
25996         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25997     },
25998     // private
25999     ClientSideMediumPassword: function (pwd)
26000     {
26001         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26002     },
26003     // private
26004     ClientSideWeakPassword: function (pwd)
26005     {
26006         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26007     }
26008           
26009 });Roo.rtf = {}; // namespace
26010 Roo.rtf.Hex = function(hex)
26011 {
26012     this.hexstr = hex;
26013 };
26014 Roo.rtf.Paragraph = function(opts)
26015 {
26016     this.content = []; ///??? is that used?
26017 };Roo.rtf.Span = function(opts)
26018 {
26019     this.value = opts.value;
26020 };
26021
26022 Roo.rtf.Group = function(parent)
26023 {
26024     // we dont want to acutally store parent - it will make debug a nightmare..
26025     this.content = [];
26026     this.cn  = [];
26027      
26028        
26029     
26030 };
26031
26032 Roo.rtf.Group.prototype = {
26033     ignorable : false,
26034     content: false,
26035     cn: false,
26036     addContent : function(node) {
26037         // could set styles...
26038         this.content.push(node);
26039     },
26040     addChild : function(cn)
26041     {
26042         this.cn.push(cn);
26043     },
26044     // only for images really...
26045     toDataURL : function()
26046     {
26047         var mimetype = false;
26048         switch(true) {
26049             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26050                 mimetype = "image/png";
26051                 break;
26052              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26053                 mimetype = "image/jpeg";
26054                 break;
26055             default :
26056                 return 'about:blank'; // ?? error?
26057         }
26058         
26059         
26060         var hexstring = this.content[this.content.length-1].value;
26061         
26062         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26063             return String.fromCharCode(parseInt(a, 16));
26064         }).join(""));
26065     }
26066     
26067 };
26068 // this looks like it's normally the {rtf{ .... }}
26069 Roo.rtf.Document = function()
26070 {
26071     // we dont want to acutally store parent - it will make debug a nightmare..
26072     this.rtlch  = [];
26073     this.content = [];
26074     this.cn = [];
26075     
26076 };
26077 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26078     addChild : function(cn)
26079     {
26080         this.cn.push(cn);
26081         switch(cn.type) {
26082             case 'rtlch': // most content seems to be inside this??
26083             case 'listtext':
26084             case 'shpinst':
26085                 this.rtlch.push(cn);
26086                 return;
26087             default:
26088                 this[cn.type] = cn;
26089         }
26090         
26091     },
26092     
26093     getElementsByType : function(type)
26094     {
26095         var ret =  [];
26096         this._getElementsByType(type, ret, this.cn, 'rtf');
26097         return ret;
26098     },
26099     _getElementsByType : function (type, ret, search_array, path)
26100     {
26101         search_array.forEach(function(n,i) {
26102             if (n.type == type) {
26103                 n.path = path + '/' + n.type + ':' + i;
26104                 ret.push(n);
26105             }
26106             if (n.cn.length > 0) {
26107                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26108             }
26109         },this);
26110     }
26111     
26112 });
26113  
26114 Roo.rtf.Ctrl = function(opts)
26115 {
26116     this.value = opts.value;
26117     this.param = opts.param;
26118 };
26119 /**
26120  *
26121  *
26122  * based on this https://github.com/iarna/rtf-parser
26123  * it's really only designed to extract pict from pasted RTF 
26124  *
26125  * usage:
26126  *
26127  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26128  *  
26129  *
26130  */
26131
26132  
26133
26134
26135
26136 Roo.rtf.Parser = function(text) {
26137     //super({objectMode: true})
26138     this.text = '';
26139     this.parserState = this.parseText;
26140     
26141     // these are for interpeter...
26142     this.doc = {};
26143     ///this.parserState = this.parseTop
26144     this.groupStack = [];
26145     this.hexStore = [];
26146     this.doc = false;
26147     
26148     this.groups = []; // where we put the return.
26149     
26150     for (var ii = 0; ii < text.length; ++ii) {
26151         ++this.cpos;
26152         
26153         if (text[ii] === '\n') {
26154             ++this.row;
26155             this.col = 1;
26156         } else {
26157             ++this.col;
26158         }
26159         this.parserState(text[ii]);
26160     }
26161     
26162     
26163     
26164 };
26165 Roo.rtf.Parser.prototype = {
26166     text : '', // string being parsed..
26167     controlWord : '',
26168     controlWordParam :  '',
26169     hexChar : '',
26170     doc : false,
26171     group: false,
26172     groupStack : false,
26173     hexStore : false,
26174     
26175     
26176     cpos : 0, 
26177     row : 1, // reportin?
26178     col : 1, //
26179
26180      
26181     push : function (el)
26182     {
26183         var m = 'cmd'+ el.type;
26184         if (typeof(this[m]) == 'undefined') {
26185             Roo.log('invalid cmd:' + el.type);
26186             return;
26187         }
26188         this[m](el);
26189         //Roo.log(el);
26190     },
26191     flushHexStore : function()
26192     {
26193         if (this.hexStore.length < 1) {
26194             return;
26195         }
26196         var hexstr = this.hexStore.map(
26197             function(cmd) {
26198                 return cmd.value;
26199         }).join('');
26200         
26201         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26202               
26203             
26204         this.hexStore.splice(0)
26205         
26206     },
26207     
26208     cmdgroupstart : function()
26209     {
26210         this.flushHexStore();
26211         if (this.group) {
26212             this.groupStack.push(this.group);
26213         }
26214          // parent..
26215         if (this.doc === false) {
26216             this.group = this.doc = new Roo.rtf.Document();
26217             return;
26218             
26219         }
26220         this.group = new Roo.rtf.Group(this.group);
26221     },
26222     cmdignorable : function()
26223     {
26224         this.flushHexStore();
26225         this.group.ignorable = true;
26226     },
26227     cmdendparagraph : function()
26228     {
26229         this.flushHexStore();
26230         this.group.addContent(new Roo.rtf.Paragraph());
26231     },
26232     cmdgroupend : function ()
26233     {
26234         this.flushHexStore();
26235         var endingGroup = this.group;
26236         
26237         
26238         this.group = this.groupStack.pop();
26239         if (this.group) {
26240             this.group.addChild(endingGroup);
26241         }
26242         
26243         
26244         
26245         var doc = this.group || this.doc;
26246         //if (endingGroup instanceof FontTable) {
26247         //  doc.fonts = endingGroup.table
26248         //} else if (endingGroup instanceof ColorTable) {
26249         //  doc.colors = endingGroup.table
26250         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26251         if (endingGroup.ignorable === false) {
26252             //code
26253             this.groups.push(endingGroup);
26254            // Roo.log( endingGroup );
26255         }
26256             //Roo.each(endingGroup.content, function(item)) {
26257             //    doc.addContent(item);
26258             //}
26259             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26260         //}
26261     },
26262     cmdtext : function (cmd)
26263     {
26264         this.flushHexStore();
26265         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26266             //this.group = this.doc
26267             return;  // we really don't care about stray text...
26268         }
26269         this.group.addContent(new Roo.rtf.Span(cmd));
26270     },
26271     cmdcontrolword : function (cmd)
26272     {
26273         this.flushHexStore();
26274         if (!this.group.type) {
26275             this.group.type = cmd.value;
26276             return;
26277         }
26278         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26279         // we actually don't care about ctrl words...
26280         return ;
26281         /*
26282         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26283         if (this[method]) {
26284             this[method](cmd.param)
26285         } else {
26286             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26287         }
26288         */
26289     },
26290     cmdhexchar : function(cmd) {
26291         this.hexStore.push(cmd);
26292     },
26293     cmderror : function(cmd) {
26294         throw cmd.value;
26295     },
26296     
26297     /*
26298       _flush (done) {
26299         if (this.text !== '\u0000') this.emitText()
26300         done()
26301       }
26302       */
26303       
26304       
26305     parseText : function(c)
26306     {
26307         if (c === '\\') {
26308             this.parserState = this.parseEscapes;
26309         } else if (c === '{') {
26310             this.emitStartGroup();
26311         } else if (c === '}') {
26312             this.emitEndGroup();
26313         } else if (c === '\x0A' || c === '\x0D') {
26314             // cr/lf are noise chars
26315         } else {
26316             this.text += c;
26317         }
26318     },
26319     
26320     parseEscapes: function (c)
26321     {
26322         if (c === '\\' || c === '{' || c === '}') {
26323             this.text += c;
26324             this.parserState = this.parseText;
26325         } else {
26326             this.parserState = this.parseControlSymbol;
26327             this.parseControlSymbol(c);
26328         }
26329     },
26330     parseControlSymbol: function(c)
26331     {
26332         if (c === '~') {
26333             this.text += '\u00a0'; // nbsp
26334             this.parserState = this.parseText
26335         } else if (c === '-') {
26336              this.text += '\u00ad'; // soft hyphen
26337         } else if (c === '_') {
26338             this.text += '\u2011'; // non-breaking hyphen
26339         } else if (c === '*') {
26340             this.emitIgnorable();
26341             this.parserState = this.parseText;
26342         } else if (c === "'") {
26343             this.parserState = this.parseHexChar;
26344         } else if (c === '|') { // formula cacter
26345             this.emitFormula();
26346             this.parserState = this.parseText;
26347         } else if (c === ':') { // subentry in an index entry
26348             this.emitIndexSubEntry();
26349             this.parserState = this.parseText;
26350         } else if (c === '\x0a') {
26351             this.emitEndParagraph();
26352             this.parserState = this.parseText;
26353         } else if (c === '\x0d') {
26354             this.emitEndParagraph();
26355             this.parserState = this.parseText;
26356         } else {
26357             this.parserState = this.parseControlWord;
26358             this.parseControlWord(c);
26359         }
26360     },
26361     parseHexChar: function (c)
26362     {
26363         if (/^[A-Fa-f0-9]$/.test(c)) {
26364             this.hexChar += c;
26365             if (this.hexChar.length >= 2) {
26366               this.emitHexChar();
26367               this.parserState = this.parseText;
26368             }
26369             return;
26370         }
26371         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26372         this.parserState = this.parseText;
26373         
26374     },
26375     parseControlWord : function(c)
26376     {
26377         if (c === ' ') {
26378             this.emitControlWord();
26379             this.parserState = this.parseText;
26380         } else if (/^[-\d]$/.test(c)) {
26381             this.parserState = this.parseControlWordParam;
26382             this.controlWordParam += c;
26383         } else if (/^[A-Za-z]$/.test(c)) {
26384           this.controlWord += c;
26385         } else {
26386           this.emitControlWord();
26387           this.parserState = this.parseText;
26388           this.parseText(c);
26389         }
26390     },
26391     parseControlWordParam : function (c) {
26392         if (/^\d$/.test(c)) {
26393           this.controlWordParam += c;
26394         } else if (c === ' ') {
26395           this.emitControlWord();
26396           this.parserState = this.parseText;
26397         } else {
26398           this.emitControlWord();
26399           this.parserState = this.parseText;
26400           this.parseText(c);
26401         }
26402     },
26403     
26404     
26405     
26406     
26407     emitText : function () {
26408         if (this.text === '') {
26409             return;
26410         }
26411         this.push({
26412             type: 'text',
26413             value: this.text,
26414             pos: this.cpos,
26415             row: this.row,
26416             col: this.col
26417         });
26418         this.text = ''
26419     },
26420     emitControlWord : function ()
26421     {
26422         this.emitText();
26423         if (this.controlWord === '') {
26424             // do we want to track this - it seems just to cause problems.
26425             //this.emitError('empty control word');
26426         } else {
26427             this.push({
26428                   type: 'controlword',
26429                   value: this.controlWord,
26430                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26431                   pos: this.cpos,
26432                   row: this.row,
26433                   col: this.col
26434             });
26435         }
26436         this.controlWord = '';
26437         this.controlWordParam = '';
26438     },
26439     emitStartGroup : function ()
26440     {
26441         this.emitText();
26442         this.push({
26443             type: 'groupstart',
26444             pos: this.cpos,
26445             row: this.row,
26446             col: this.col
26447         });
26448     },
26449     emitEndGroup : function ()
26450     {
26451         this.emitText();
26452         this.push({
26453             type: 'groupend',
26454             pos: this.cpos,
26455             row: this.row,
26456             col: this.col
26457         });
26458     },
26459     emitIgnorable : function ()
26460     {
26461         this.emitText();
26462         this.push({
26463             type: 'ignorable',
26464             pos: this.cpos,
26465             row: this.row,
26466             col: this.col
26467         });
26468     },
26469     emitHexChar : function ()
26470     {
26471         this.emitText();
26472         this.push({
26473             type: 'hexchar',
26474             value: this.hexChar,
26475             pos: this.cpos,
26476             row: this.row,
26477             col: this.col
26478         });
26479         this.hexChar = ''
26480     },
26481     emitError : function (message)
26482     {
26483       this.emitText();
26484       this.push({
26485             type: 'error',
26486             value: message,
26487             row: this.row,
26488             col: this.col,
26489             char: this.cpos //,
26490             //stack: new Error().stack
26491         });
26492     },
26493     emitEndParagraph : function () {
26494         this.emitText();
26495         this.push({
26496             type: 'endparagraph',
26497             pos: this.cpos,
26498             row: this.row,
26499             col: this.col
26500         });
26501     }
26502      
26503 } ; 
26504 /**
26505  * @class Roo.htmleditor.Filter
26506  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26507  * @cfg {DomElement} node The node to iterate and filter
26508  * @cfg {boolean|String|Array} tag Tags to replace 
26509  * @constructor
26510  * Create a new Filter.
26511  * @param {Object} config Configuration options
26512  */
26513
26514
26515
26516 Roo.htmleditor.Filter = function(cfg) {
26517     Roo.apply(this.cfg);
26518     // this does not actually call walk as it's really just a abstract class
26519 }
26520
26521
26522 Roo.htmleditor.Filter.prototype = {
26523     
26524     node: false,
26525     
26526     tag: false,
26527
26528     // overrride to do replace comments.
26529     replaceComment : false,
26530     
26531     // overrride to do replace or do stuff with tags..
26532     replaceTag : false,
26533     
26534     walk : function(dom)
26535     {
26536         Roo.each( Array.from(dom.childNodes), function( e ) {
26537             switch(true) {
26538                 
26539                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26540                     this.replaceComment(e);
26541                     return;
26542                 
26543                 case e.nodeType != 1: //not a node.
26544                     return;
26545                 
26546                 case this.tag === true: // everything
26547                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26548                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26549                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26550                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26551                     if (this.replaceTag && false === this.replaceTag(e)) {
26552                         return;
26553                     }
26554                     if (e.hasChildNodes()) {
26555                         this.walk(e);
26556                     }
26557                     return;
26558                 
26559                 default:    // tags .. that do not match.
26560                     if (e.hasChildNodes()) {
26561                         this.walk(e);
26562                     }
26563             }
26564             
26565         }, this);
26566         
26567     },
26568     
26569     
26570     removeNodeKeepChildren : function( node)
26571     {
26572     
26573         ar = Array.from(node.childNodes);
26574         for (var i = 0; i < ar.length; i++) {
26575          
26576             node.removeChild(ar[i]);
26577             // what if we need to walk these???
26578             node.parentNode.insertBefore(ar[i], node);
26579            
26580         }
26581         node.parentNode.removeChild(node);
26582     }
26583 }; 
26584
26585 /**
26586  * @class Roo.htmleditor.FilterAttributes
26587  * clean attributes and  styles including http:// etc.. in attribute
26588  * @constructor
26589 * Run a new Attribute Filter
26590 * @param {Object} config Configuration options
26591  */
26592 Roo.htmleditor.FilterAttributes = function(cfg)
26593 {
26594     Roo.apply(this, cfg);
26595     this.attrib_black = this.attrib_black || [];
26596     this.attrib_white = this.attrib_white || [];
26597
26598     this.attrib_clean = this.attrib_clean || [];
26599     this.style_white = this.style_white || [];
26600     this.style_black = this.style_black || [];
26601     this.walk(cfg.node);
26602 }
26603
26604 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26605 {
26606     tag: true, // all tags
26607     
26608     attrib_black : false, // array
26609     attrib_clean : false,
26610     attrib_white : false,
26611
26612     style_white : false,
26613     style_black : false,
26614      
26615      
26616     replaceTag : function(node)
26617     {
26618         if (!node.attributes || !node.attributes.length) {
26619             return true;
26620         }
26621         
26622         for (var i = node.attributes.length-1; i > -1 ; i--) {
26623             var a = node.attributes[i];
26624             //console.log(a);
26625             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26626                 node.removeAttribute(a.name);
26627                 continue;
26628             }
26629             
26630             
26631             
26632             if (a.name.toLowerCase().substr(0,2)=='on')  {
26633                 node.removeAttribute(a.name);
26634                 continue;
26635             }
26636             
26637             
26638             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26639                 node.removeAttribute(a.name);
26640                 continue;
26641             }
26642             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26643                 this.cleanAttr(node,a.name,a.value); // fixme..
26644                 continue;
26645             }
26646             if (a.name == 'style') {
26647                 this.cleanStyle(node,a.name,a.value);
26648                 continue;
26649             }
26650             /// clean up MS crap..
26651             // tecnically this should be a list of valid class'es..
26652             
26653             
26654             if (a.name == 'class') {
26655                 if (a.value.match(/^Mso/)) {
26656                     node.removeAttribute('class');
26657                 }
26658                 
26659                 if (a.value.match(/^body$/)) {
26660                     node.removeAttribute('class');
26661                 }
26662                 continue;
26663             }
26664             
26665             
26666             // style cleanup!?
26667             // class cleanup?
26668             
26669         }
26670         return true; // clean children
26671     },
26672         
26673     cleanAttr: function(node, n,v)
26674     {
26675         
26676         if (v.match(/^\./) || v.match(/^\//)) {
26677             return;
26678         }
26679         if (v.match(/^(http|https):\/\//)
26680             || v.match(/^mailto:/) 
26681             || v.match(/^ftp:/)
26682             || v.match(/^data:/)
26683             ) {
26684             return;
26685         }
26686         if (v.match(/^#/)) {
26687             return;
26688         }
26689         if (v.match(/^\{/)) { // allow template editing.
26690             return;
26691         }
26692 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26693         node.removeAttribute(n);
26694         
26695     },
26696     cleanStyle : function(node,  n,v)
26697     {
26698         if (v.match(/expression/)) { //XSS?? should we even bother..
26699             node.removeAttribute(n);
26700             return;
26701         }
26702         
26703         var parts = v.split(/;/);
26704         var clean = [];
26705         
26706         Roo.each(parts, function(p) {
26707             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26708             if (!p.length) {
26709                 return true;
26710             }
26711             var l = p.split(':').shift().replace(/\s+/g,'');
26712             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26713             
26714             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26715                 return true;
26716             }
26717             //Roo.log()
26718             // only allow 'c whitelisted system attributes'
26719             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26720                 return true;
26721             }
26722             
26723             
26724             clean.push(p);
26725             return true;
26726         },this);
26727         if (clean.length) { 
26728             node.setAttribute(n, clean.join(';'));
26729         } else {
26730             node.removeAttribute(n);
26731         }
26732         
26733     }
26734         
26735         
26736         
26737     
26738 });/**
26739  * @class Roo.htmleditor.FilterBlack
26740  * remove blacklisted elements.
26741  * @constructor
26742  * Run a new Blacklisted Filter
26743  * @param {Object} config Configuration options
26744  */
26745
26746 Roo.htmleditor.FilterBlack = function(cfg)
26747 {
26748     Roo.apply(this, cfg);
26749     this.walk(cfg.node);
26750 }
26751
26752 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26753 {
26754     tag : true, // all elements.
26755    
26756     replaceTag : function(n)
26757     {
26758         n.parentNode.removeChild(n);
26759     }
26760 });
26761 /**
26762  * @class Roo.htmleditor.FilterComment
26763  * remove comments.
26764  * @constructor
26765 * Run a new Comments Filter
26766 * @param {Object} config Configuration options
26767  */
26768 Roo.htmleditor.FilterComment = function(cfg)
26769 {
26770     this.walk(cfg.node);
26771 }
26772
26773 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26774 {
26775   
26776     replaceComment : function(n)
26777     {
26778         n.parentNode.removeChild(n);
26779     }
26780 });/**
26781  * @class Roo.htmleditor.FilterKeepChildren
26782  * remove tags but keep children
26783  * @constructor
26784  * Run a new Keep Children Filter
26785  * @param {Object} config Configuration options
26786  */
26787
26788 Roo.htmleditor.FilterKeepChildren = function(cfg)
26789 {
26790     Roo.apply(this, cfg);
26791     if (this.tag === false) {
26792         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26793     }
26794     // hacky?
26795     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26796         this.cleanNamespace = true;
26797     }
26798         
26799     this.walk(cfg.node);
26800 }
26801
26802 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26803 {
26804     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26805   
26806     replaceTag : function(node)
26807     {
26808         // walk children...
26809         //Roo.log(node.tagName);
26810         var ar = Array.from(node.childNodes);
26811         //remove first..
26812         
26813         for (var i = 0; i < ar.length; i++) {
26814             var e = ar[i];
26815             if (e.nodeType == 1) {
26816                 if (
26817                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26818                     || // array and it matches
26819                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26820                     ||
26821                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26822                     ||
26823                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26824                 ) {
26825                     this.replaceTag(ar[i]); // child is blacklisted as well...
26826                     continue;
26827                 }
26828             }
26829         }  
26830         ar = Array.from(node.childNodes);
26831         for (var i = 0; i < ar.length; i++) {
26832          
26833             node.removeChild(ar[i]);
26834             // what if we need to walk these???
26835             node.parentNode.insertBefore(ar[i], node);
26836             if (this.tag !== false) {
26837                 this.walk(ar[i]);
26838                 
26839             }
26840         }
26841         //Roo.log("REMOVE:" + node.tagName);
26842         node.parentNode.removeChild(node);
26843         return false; // don't walk children
26844         
26845         
26846     }
26847 });/**
26848  * @class Roo.htmleditor.FilterParagraph
26849  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26850  * like on 'push' to remove the <p> tags and replace them with line breaks.
26851  * @constructor
26852  * Run a new Paragraph Filter
26853  * @param {Object} config Configuration options
26854  */
26855
26856 Roo.htmleditor.FilterParagraph = function(cfg)
26857 {
26858     // no need to apply config.
26859     this.walk(cfg.node);
26860 }
26861
26862 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26863 {
26864     
26865      
26866     tag : 'P',
26867     
26868      
26869     replaceTag : function(node)
26870     {
26871         
26872         if (node.childNodes.length == 1 &&
26873             node.childNodes[0].nodeType == 3 &&
26874             node.childNodes[0].textContent.trim().length < 1
26875             ) {
26876             // remove and replace with '<BR>';
26877             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26878             return false; // no need to walk..
26879         }
26880         var ar = Array.from(node.childNodes);
26881         for (var i = 0; i < ar.length; i++) {
26882             node.removeChild(ar[i]);
26883             // what if we need to walk these???
26884             node.parentNode.insertBefore(ar[i], node);
26885         }
26886         // now what about this?
26887         // <p> &nbsp; </p>
26888         
26889         // double BR.
26890         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26891         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26892         node.parentNode.removeChild(node);
26893         
26894         return false;
26895
26896     }
26897     
26898 });/**
26899  * @class Roo.htmleditor.FilterSpan
26900  * filter span's with no attributes out..
26901  * @constructor
26902  * Run a new Span Filter
26903  * @param {Object} config Configuration options
26904  */
26905
26906 Roo.htmleditor.FilterSpan = function(cfg)
26907 {
26908     // no need to apply config.
26909     this.walk(cfg.node);
26910 }
26911
26912 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26913 {
26914      
26915     tag : 'SPAN',
26916      
26917  
26918     replaceTag : function(node)
26919     {
26920         if (node.attributes && node.attributes.length > 0) {
26921             return true; // walk if there are any.
26922         }
26923         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26924         return false;
26925      
26926     }
26927     
26928 });/**
26929  * @class Roo.htmleditor.FilterTableWidth
26930   try and remove table width data - as that frequently messes up other stuff.
26931  * 
26932  *      was cleanTableWidths.
26933  *
26934  * Quite often pasting from word etc.. results in tables with column and widths.
26935  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26936  *
26937  * @constructor
26938  * Run a new Table Filter
26939  * @param {Object} config Configuration options
26940  */
26941
26942 Roo.htmleditor.FilterTableWidth = function(cfg)
26943 {
26944     // no need to apply config.
26945     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26946     this.walk(cfg.node);
26947 }
26948
26949 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26950 {
26951      
26952      
26953     
26954     replaceTag: function(node) {
26955         
26956         
26957       
26958         if (node.hasAttribute('width')) {
26959             node.removeAttribute('width');
26960         }
26961         
26962          
26963         if (node.hasAttribute("style")) {
26964             // pretty basic...
26965             
26966             var styles = node.getAttribute("style").split(";");
26967             var nstyle = [];
26968             Roo.each(styles, function(s) {
26969                 if (!s.match(/:/)) {
26970                     return;
26971                 }
26972                 var kv = s.split(":");
26973                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26974                     return;
26975                 }
26976                 // what ever is left... we allow.
26977                 nstyle.push(s);
26978             });
26979             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26980             if (!nstyle.length) {
26981                 node.removeAttribute('style');
26982             }
26983         }
26984         
26985         return true; // continue doing children..
26986     }
26987 });/**
26988  * @class Roo.htmleditor.FilterWord
26989  * try and clean up all the mess that Word generates.
26990  * 
26991  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26992  
26993  * @constructor
26994  * Run a new Span Filter
26995  * @param {Object} config Configuration options
26996  */
26997
26998 Roo.htmleditor.FilterWord = function(cfg)
26999 {
27000     // no need to apply config.
27001     this.replaceDocBullets(cfg.node);
27002     
27003     this.replaceAname(cfg.node);
27004     // this is disabled as the removal is done by other filters;
27005    // this.walk(cfg.node);
27006     this.replaceImageTable(cfg.node);
27007     
27008 }
27009
27010 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27011 {
27012     tag: true,
27013      
27014     
27015     /**
27016      * Clean up MS wordisms...
27017      */
27018     replaceTag : function(node)
27019     {
27020          
27021         // no idea what this does - span with text, replaceds with just text.
27022         if(
27023                 node.nodeName == 'SPAN' &&
27024                 !node.hasAttributes() &&
27025                 node.childNodes.length == 1 &&
27026                 node.firstChild.nodeName == "#text"  
27027         ) {
27028             var textNode = node.firstChild;
27029             node.removeChild(textNode);
27030             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27031                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27032             }
27033             node.parentNode.insertBefore(textNode, node);
27034             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27035                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27036             }
27037             
27038             node.parentNode.removeChild(node);
27039             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27040         }
27041         
27042    
27043         
27044         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27045             node.parentNode.removeChild(node);
27046             return false; // dont do chidlren
27047         }
27048         //Roo.log(node.tagName);
27049         // remove - but keep children..
27050         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27051             //Roo.log('-- removed');
27052             while (node.childNodes.length) {
27053                 var cn = node.childNodes[0];
27054                 node.removeChild(cn);
27055                 node.parentNode.insertBefore(cn, node);
27056                 // move node to parent - and clean it..
27057                 if (cn.nodeType == 1) {
27058                     this.replaceTag(cn);
27059                 }
27060                 
27061             }
27062             node.parentNode.removeChild(node);
27063             /// no need to iterate chidlren = it's got none..
27064             //this.iterateChildren(node, this.cleanWord);
27065             return false; // no need to iterate children.
27066         }
27067         // clean styles
27068         if (node.className.length) {
27069             
27070             var cn = node.className.split(/\W+/);
27071             var cna = [];
27072             Roo.each(cn, function(cls) {
27073                 if (cls.match(/Mso[a-zA-Z]+/)) {
27074                     return;
27075                 }
27076                 cna.push(cls);
27077             });
27078             node.className = cna.length ? cna.join(' ') : '';
27079             if (!cna.length) {
27080                 node.removeAttribute("class");
27081             }
27082         }
27083         
27084         if (node.hasAttribute("lang")) {
27085             node.removeAttribute("lang");
27086         }
27087         
27088         if (node.hasAttribute("style")) {
27089             
27090             var styles = node.getAttribute("style").split(";");
27091             var nstyle = [];
27092             Roo.each(styles, function(s) {
27093                 if (!s.match(/:/)) {
27094                     return;
27095                 }
27096                 var kv = s.split(":");
27097                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27098                     return;
27099                 }
27100                 // what ever is left... we allow.
27101                 nstyle.push(s);
27102             });
27103             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27104             if (!nstyle.length) {
27105                 node.removeAttribute('style');
27106             }
27107         }
27108         return true; // do children
27109         
27110         
27111         
27112     },
27113     
27114     styleToObject: function(node)
27115     {
27116         var styles = (node.getAttribute("style") || '').split(";");
27117         var ret = {};
27118         Roo.each(styles, function(s) {
27119             if (!s.match(/:/)) {
27120                 return;
27121             }
27122             var kv = s.split(":");
27123              
27124             // what ever is left... we allow.
27125             ret[kv[0].trim()] = kv[1];
27126         });
27127         return ret;
27128     },
27129     
27130     
27131     replaceAname : function (doc)
27132     {
27133         // replace all the a/name without..
27134         var aa = Array.from(doc.getElementsByTagName('a'));
27135         for (var i = 0; i  < aa.length; i++) {
27136             var a = aa[i];
27137             if (a.hasAttribute("name")) {
27138                 a.removeAttribute("name");
27139             }
27140             if (a.hasAttribute("href")) {
27141                 continue;
27142             }
27143             // reparent children.
27144             this.removeNodeKeepChildren(a);
27145             
27146         }
27147         
27148         
27149         
27150     },
27151
27152     
27153     
27154     replaceDocBullets : function(doc)
27155     {
27156         // this is a bit odd - but it appears some indents use ql-indent-1
27157          //Roo.log(doc.innerHTML);
27158         
27159         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27160         for( var i = 0; i < listpara.length; i ++) {
27161             listpara[i].className = "MsoListParagraph";
27162         }
27163         
27164         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27165         for( var i = 0; i < listpara.length; i ++) {
27166             listpara[i].className = "MsoListParagraph";
27167         }
27168         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27169         for( var i = 0; i < listpara.length; i ++) {
27170             listpara[i].className = "MsoListParagraph";
27171         }
27172         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27173         for( var i = 0; i < listpara.length; i ++) {
27174             listpara[i].className = "MsoListParagraph";
27175         }
27176         
27177         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27178         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27179         for( var i = 0; i < htwo.length; i ++) {
27180             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27181                 htwo[i].className = "MsoListParagraph";
27182             }
27183         }
27184         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27185         for( var i = 0; i < listpara.length; i ++) {
27186             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27187                 listpara[i].className = "MsoListParagraph";
27188             } else {
27189                 listpara[i].className = "MsoNormalx";
27190             }
27191         }
27192        
27193         listpara = doc.getElementsByClassName('MsoListParagraph');
27194         // Roo.log(doc.innerHTML);
27195         
27196         
27197         
27198         while(listpara.length) {
27199             
27200             this.replaceDocBullet(listpara.item(0));
27201         }
27202       
27203     },
27204     
27205      
27206     
27207     replaceDocBullet : function(p)
27208     {
27209         // gather all the siblings.
27210         var ns = p,
27211             parent = p.parentNode,
27212             doc = parent.ownerDocument,
27213             items = [];
27214          
27215         //Roo.log("Parsing: " + p.innerText)    ;
27216         var listtype = 'ul';   
27217         while (ns) {
27218             if (ns.nodeType != 1) {
27219                 ns = ns.nextSibling;
27220                 continue;
27221             }
27222             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27223                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27224                 break;
27225             }
27226             var spans = ns.getElementsByTagName('span');
27227             
27228             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27229                 items.push(ns);
27230                 ns = ns.nextSibling;
27231                 has_list = true;
27232                 if (!spans.length) {
27233                     continue;
27234                 }
27235                 var ff = '';
27236                 var se = spans[0];
27237                 for (var i = 0; i < spans.length;i++) {
27238                     se = spans[i];
27239                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27240                         ff = se.style.fontFamily;
27241                         break;
27242                     }
27243                 }
27244                  
27245                     
27246                 //Roo.log("got font family: " + ff);
27247                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27248                     listtype = 'ol';
27249                 }
27250                 
27251                 continue;
27252             }
27253             //Roo.log("no mso-list?");
27254             
27255             var spans = ns.getElementsByTagName('span');
27256             if (!spans.length) {
27257                 break;
27258             }
27259             var has_list  = false;
27260             for(var i = 0; i < spans.length; i++) {
27261                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27262                     has_list = true;
27263                     break;
27264                 }
27265             }
27266             if (!has_list) {
27267                 break;
27268             }
27269             items.push(ns);
27270             ns = ns.nextSibling;
27271             
27272             
27273         }
27274         if (!items.length) {
27275             ns.className = "";
27276             return;
27277         }
27278         
27279         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27280         parent.insertBefore(ul, p);
27281         var lvl = 0;
27282         var stack = [ ul ];
27283         var last_li = false;
27284         
27285         var margin_to_depth = {};
27286         max_margins = -1;
27287         
27288         items.forEach(function(n, ipos) {
27289             //Roo.log("got innertHMLT=" + n.innerHTML);
27290             
27291             var spans = n.getElementsByTagName('span');
27292             if (!spans.length) {
27293                 //Roo.log("No spans found");
27294                  
27295                 parent.removeChild(n);
27296                 
27297                 
27298                 return; // skip it...
27299             }
27300            
27301                 
27302             var num = 1;
27303             var style = {};
27304             for(var i = 0; i < spans.length; i++) {
27305             
27306                 style = this.styleToObject(spans[i]);
27307                 if (typeof(style['mso-list']) == 'undefined') {
27308                     continue;
27309                 }
27310                 if (listtype == 'ol') {
27311                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27312                 }
27313                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27314                 break;
27315             }
27316             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27317             style = this.styleToObject(n); // mo-list is from the parent node.
27318             if (typeof(style['mso-list']) == 'undefined') {
27319                 //Roo.log("parent is missing level");
27320                   
27321                 parent.removeChild(n);
27322                  
27323                 return;
27324             }
27325             
27326             var margin = style['margin-left'];
27327             if (typeof(margin_to_depth[margin]) == 'undefined') {
27328                 max_margins++;
27329                 margin_to_depth[margin] = max_margins;
27330             }
27331             nlvl = margin_to_depth[margin] ;
27332              
27333             if (nlvl > lvl) {
27334                 //new indent
27335                 var nul = doc.createElement(listtype); // what about number lists...
27336                 if (!last_li) {
27337                     last_li = doc.createElement('li');
27338                     stack[lvl].appendChild(last_li);
27339                 }
27340                 last_li.appendChild(nul);
27341                 stack[nlvl] = nul;
27342                 
27343             }
27344             lvl = nlvl;
27345             
27346             // not starting at 1..
27347             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27348                 stack[nlvl].setAttribute("start", num);
27349             }
27350             
27351             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27352             last_li = nli;
27353             nli.innerHTML = n.innerHTML;
27354             //Roo.log("innerHTML = " + n.innerHTML);
27355             parent.removeChild(n);
27356             
27357              
27358              
27359             
27360         },this);
27361         
27362         
27363         
27364         
27365     },
27366     
27367     replaceImageTable : function(doc)
27368     {
27369          /*
27370           <table cellpadding=0 cellspacing=0 align=left>
27371   <tr>
27372    <td width=423 height=0></td>
27373   </tr>
27374   <tr>
27375    <td></td>
27376    <td><img width=601 height=401
27377    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27378    v:shapes="Picture_x0020_2"></td>
27379   </tr>
27380  </table>
27381  */
27382         var imgs = Array.from(doc.getElementsByTagName('img'));
27383         Roo.each(imgs, function(img) {
27384             var td = img.parentNode;
27385             if (td.nodeName !=  'TD') {
27386                 return;
27387             }
27388             var tr = td.parentNode;
27389             if (tr.nodeName !=  'TR') {
27390                 return;
27391             }
27392             var tbody = tr.parentNode;
27393             if (tbody.nodeName !=  'TBODY') {
27394                 return;
27395             }
27396             var table = tbody.parentNode;
27397             if (table.nodeName !=  'TABLE') {
27398                 return;
27399             }
27400             // first row..
27401             
27402             if (table.getElementsByTagName('tr').length != 2) {
27403                 return;
27404             }
27405             if (table.getElementsByTagName('td').length != 3) {
27406                 return;
27407             }
27408             if (table.innerText.trim() != '') {
27409                 return;
27410             }
27411             var p = table.parentNode;
27412             img.parentNode.removeChild(img);
27413             p.insertBefore(img, table);
27414             p.removeChild(table);
27415             
27416             
27417             
27418         });
27419         
27420       
27421     }
27422     
27423 });
27424 /**
27425  * @class Roo.htmleditor.FilterStyleToTag
27426  * part of the word stuff... - certain 'styles' should be converted to tags.
27427  * eg.
27428  *   font-weight: bold -> bold
27429  *   ?? super / subscrit etc..
27430  * 
27431  * @constructor
27432 * Run a new style to tag filter.
27433 * @param {Object} config Configuration options
27434  */
27435 Roo.htmleditor.FilterStyleToTag = function(cfg)
27436 {
27437     
27438     this.tags = {
27439         B  : [ 'fontWeight' , 'bold'],
27440         I :  [ 'fontStyle' , 'italic'],
27441         //pre :  [ 'font-style' , 'italic'],
27442         // h1.. h6 ?? font-size?
27443         SUP : [ 'verticalAlign' , 'super' ],
27444         SUB : [ 'verticalAlign' , 'sub' ]
27445         
27446         
27447     };
27448     
27449     Roo.apply(this, cfg);
27450      
27451     
27452     this.walk(cfg.node);
27453     
27454     
27455     
27456 }
27457
27458
27459 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27460 {
27461     tag: true, // all tags
27462     
27463     tags : false,
27464     
27465     
27466     replaceTag : function(node)
27467     {
27468         
27469         
27470         if (node.getAttribute("style") === null) {
27471             return true;
27472         }
27473         var inject = [];
27474         for (var k in this.tags) {
27475             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27476                 inject.push(k);
27477                 node.style.removeProperty(this.tags[k][0]);
27478             }
27479         }
27480         if (!inject.length) {
27481             return true; 
27482         }
27483         var cn = Array.from(node.childNodes);
27484         var nn = node;
27485         Roo.each(inject, function(t) {
27486             var nc = node.ownerDocument.createElement(t);
27487             nn.appendChild(nc);
27488             nn = nc;
27489         });
27490         for(var i = 0;i < cn.length;cn++) {
27491             node.removeChild(cn[i]);
27492             nn.appendChild(cn[i]);
27493         }
27494         return true /// iterate thru
27495     }
27496     
27497 })/**
27498  * @class Roo.htmleditor.FilterLongBr
27499  * BR/BR/BR - keep a maximum of 2...
27500  * @constructor
27501  * Run a new Long BR Filter
27502  * @param {Object} config Configuration options
27503  */
27504
27505 Roo.htmleditor.FilterLongBr = function(cfg)
27506 {
27507     // no need to apply config.
27508     this.walk(cfg.node);
27509 }
27510
27511 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27512 {
27513     
27514      
27515     tag : 'BR',
27516     
27517      
27518     replaceTag : function(node)
27519     {
27520         
27521         var ps = node.nextSibling;
27522         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27523             ps = ps.nextSibling;
27524         }
27525         
27526         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27527             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27528             return false;
27529         }
27530         
27531         if (!ps || ps.nodeType != 1) {
27532             return false;
27533         }
27534         
27535         if (!ps || ps.tagName != 'BR') {
27536            
27537             return false;
27538         }
27539         
27540         
27541         
27542         
27543         
27544         if (!node.previousSibling) {
27545             return false;
27546         }
27547         var ps = node.previousSibling;
27548         
27549         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27550             ps = ps.previousSibling;
27551         }
27552         if (!ps || ps.nodeType != 1) {
27553             return false;
27554         }
27555         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27556         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27557             return false;
27558         }
27559         
27560         node.parentNode.removeChild(node); // remove me...
27561         
27562         return false; // no need to do children
27563
27564     }
27565     
27566 }); 
27567
27568 /**
27569  * @class Roo.htmleditor.FilterBlock
27570  * removes id / data-block and contenteditable that are associated with blocks
27571  * usage should be done on a cloned copy of the dom
27572  * @constructor
27573 * Run a new Attribute Filter { node : xxxx }}
27574 * @param {Object} config Configuration options
27575  */
27576 Roo.htmleditor.FilterBlock = function(cfg)
27577 {
27578     Roo.apply(this, cfg);
27579     var qa = cfg.node.querySelectorAll;
27580     this.removeAttributes('data-block');
27581     this.removeAttributes('contenteditable');
27582     this.removeAttributes('id');
27583     
27584 }
27585
27586 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27587 {
27588     node: true, // all tags
27589      
27590      
27591     removeAttributes : function(attr)
27592     {
27593         var ar = this.node.querySelectorAll('*[' + attr + ']');
27594         for (var i =0;i<ar.length;i++) {
27595             ar[i].removeAttribute(attr);
27596         }
27597     }
27598         
27599         
27600         
27601     
27602 });
27603 /***
27604  * This is based loosely on tinymce 
27605  * @class Roo.htmleditor.TidySerializer
27606  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27607  * @constructor
27608  * @method Serializer
27609  * @param {Object} settings Name/value settings object.
27610  */
27611
27612
27613 Roo.htmleditor.TidySerializer = function(settings)
27614 {
27615     Roo.apply(this, settings);
27616     
27617     this.writer = new Roo.htmleditor.TidyWriter(settings);
27618     
27619     
27620
27621 };
27622 Roo.htmleditor.TidySerializer.prototype = {
27623     
27624     /**
27625      * @param {boolean} inner do the inner of the node.
27626      */
27627     inner : false,
27628     
27629     writer : false,
27630     
27631     /**
27632     * Serializes the specified node into a string.
27633     *
27634     * @example
27635     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27636     * @method serialize
27637     * @param {DomElement} node Node instance to serialize.
27638     * @return {String} String with HTML based on DOM tree.
27639     */
27640     serialize : function(node) {
27641         
27642         // = settings.validate;
27643         var writer = this.writer;
27644         var self  = this;
27645         this.handlers = {
27646             // #text
27647             3: function(node) {
27648                 
27649                 writer.text(node.nodeValue, node);
27650             },
27651             // #comment
27652             8: function(node) {
27653                 writer.comment(node.nodeValue);
27654             },
27655             // Processing instruction
27656             7: function(node) {
27657                 writer.pi(node.name, node.nodeValue);
27658             },
27659             // Doctype
27660             10: function(node) {
27661                 writer.doctype(node.nodeValue);
27662             },
27663             // CDATA
27664             4: function(node) {
27665                 writer.cdata(node.nodeValue);
27666             },
27667             // Document fragment
27668             11: function(node) {
27669                 node = node.firstChild;
27670                 if (!node) {
27671                     return;
27672                 }
27673                 while(node) {
27674                     self.walk(node);
27675                     node = node.nextSibling
27676                 }
27677             }
27678         };
27679         writer.reset();
27680         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27681         return writer.getContent();
27682     },
27683
27684     walk: function(node)
27685     {
27686         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27687             handler = this.handlers[node.nodeType];
27688             
27689         if (handler) {
27690             handler(node);
27691             return;
27692         }
27693     
27694         var name = node.nodeName;
27695         var isEmpty = node.childNodes.length < 1;
27696       
27697         var writer = this.writer;
27698         var attrs = node.attributes;
27699         // Sort attributes
27700         
27701         writer.start(node.nodeName, attrs, isEmpty, node);
27702         if (isEmpty) {
27703             return;
27704         }
27705         node = node.firstChild;
27706         if (!node) {
27707             writer.end(name);
27708             return;
27709         }
27710         while (node) {
27711             this.walk(node);
27712             node = node.nextSibling;
27713         }
27714         writer.end(name);
27715         
27716     
27717     }
27718     // Serialize element and treat all non elements as fragments
27719    
27720 }; 
27721
27722 /***
27723  * This is based loosely on tinymce 
27724  * @class Roo.htmleditor.TidyWriter
27725  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27726  *
27727  * Known issues?
27728  * - not tested much with 'PRE' formated elements.
27729  * 
27730  *
27731  *
27732  */
27733
27734 Roo.htmleditor.TidyWriter = function(settings)
27735 {
27736     
27737     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27738     Roo.apply(this, settings);
27739     this.html = [];
27740     this.state = [];
27741      
27742     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27743   
27744 }
27745 Roo.htmleditor.TidyWriter.prototype = {
27746
27747  
27748     state : false,
27749     
27750     indent :  '  ',
27751     
27752     // part of state...
27753     indentstr : '',
27754     in_pre: false,
27755     in_inline : false,
27756     last_inline : false,
27757     encode : false,
27758      
27759     
27760             /**
27761     * Writes the a start element such as <p id="a">.
27762     *
27763     * @method start
27764     * @param {String} name Name of the element.
27765     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27766     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27767     */
27768     start: function(name, attrs, empty, node)
27769     {
27770         var i, l, attr, value;
27771         
27772         // there are some situations where adding line break && indentation will not work. will not work.
27773         // <span / b / i ... formating?
27774         
27775         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27776         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27777         
27778         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27779         
27780         var add_lb = name == 'BR' ? false : in_inline;
27781         
27782         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27783             i_inline = false;
27784         }
27785
27786         var indentstr =  this.indentstr;
27787         
27788         // e_inline = elements that can be inline, but still allow \n before and after?
27789         // only 'BR' ??? any others?
27790         
27791         // ADD LINE BEFORE tage
27792         if (!this.in_pre) {
27793             if (in_inline) {
27794                 //code
27795                 if (name == 'BR') {
27796                     this.addLine();
27797                 } else if (this.lastElementEndsWS()) {
27798                     this.addLine();
27799                 } else{
27800                     // otherwise - no new line. (and dont indent.)
27801                     indentstr = '';
27802                 }
27803                 
27804             } else {
27805                 this.addLine();
27806             }
27807         } else {
27808             indentstr = '';
27809         }
27810         
27811         this.html.push(indentstr + '<', name.toLowerCase());
27812         
27813         if (attrs) {
27814             for (i = 0, l = attrs.length; i < l; i++) {
27815                 attr = attrs[i];
27816                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27817             }
27818         }
27819      
27820         if (empty) {
27821             if (is_short) {
27822                 this.html[this.html.length] = '/>';
27823             } else {
27824                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27825             }
27826             var e_inline = name == 'BR' ? false : this.in_inline;
27827             
27828             if (!e_inline && !this.in_pre) {
27829                 this.addLine();
27830             }
27831             return;
27832         
27833         }
27834         // not empty..
27835         this.html[this.html.length] = '>';
27836         
27837         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27838         /*
27839         if (!in_inline && !in_pre) {
27840             var cn = node.firstChild;
27841             while(cn) {
27842                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27843                     in_inline = true
27844                     break;
27845                 }
27846                 cn = cn.nextSibling;
27847             }
27848              
27849         }
27850         */
27851         
27852         
27853         this.pushState({
27854             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27855             in_pre : in_pre,
27856             in_inline :  in_inline
27857         });
27858         // add a line after if we are not in a
27859         
27860         if (!in_inline && !in_pre) {
27861             this.addLine();
27862         }
27863         
27864             
27865          
27866         
27867     },
27868     
27869     lastElementEndsWS : function()
27870     {
27871         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27872         if (value === false) {
27873             return true;
27874         }
27875         return value.match(/\s+$/);
27876         
27877     },
27878     
27879     /**
27880      * Writes the a end element such as </p>.
27881      *
27882      * @method end
27883      * @param {String} name Name of the element.
27884      */
27885     end: function(name) {
27886         var value;
27887         this.popState();
27888         var indentstr = '';
27889         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27890         
27891         if (!this.in_pre && !in_inline) {
27892             this.addLine();
27893             indentstr  = this.indentstr;
27894         }
27895         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27896         this.last_inline = in_inline;
27897         
27898         // pop the indent state..
27899     },
27900     /**
27901      * Writes a text node.
27902      *
27903      * In pre - we should not mess with the contents.
27904      * 
27905      *
27906      * @method text
27907      * @param {String} text String to write out.
27908      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27909      */
27910     text: function(in_text, node)
27911     {
27912         // if not in whitespace critical
27913         if (in_text.length < 1) {
27914             return;
27915         }
27916         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27917         
27918         if (this.in_pre) {
27919             this.html[this.html.length] =  text;
27920             return;   
27921         }
27922         
27923         if (this.in_inline) {
27924             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27925             if (text != ' ') {
27926                 text = text.replace(/\s+/,' ');  // all white space to single white space
27927                 
27928                     
27929                 // if next tag is '<BR>', then we can trim right..
27930                 if (node.nextSibling &&
27931                     node.nextSibling.nodeType == 1 &&
27932                     node.nextSibling.nodeName == 'BR' )
27933                 {
27934                     text = text.replace(/\s+$/g,'');
27935                 }
27936                 // if previous tag was a BR, we can also trim..
27937                 if (node.previousSibling &&
27938                     node.previousSibling.nodeType == 1 &&
27939                     node.previousSibling.nodeName == 'BR' )
27940                 {
27941                     text = this.indentstr +  text.replace(/^\s+/g,'');
27942                 }
27943                 if (text.match(/\n/)) {
27944                     text = text.replace(
27945                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27946                     );
27947                     // remoeve the last whitespace / line break.
27948                     text = text.replace(/\n\s+$/,'');
27949                 }
27950                 // repace long lines
27951                 
27952             }
27953              
27954             this.html[this.html.length] =  text;
27955             return;   
27956         }
27957         // see if previous element was a inline element.
27958         var indentstr = this.indentstr;
27959    
27960         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27961         
27962         // should trim left?
27963         if (node.previousSibling &&
27964             node.previousSibling.nodeType == 1 &&
27965             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27966         {
27967             indentstr = '';
27968             
27969         } else {
27970             this.addLine();
27971             text = text.replace(/^\s+/,''); // trim left
27972           
27973         }
27974         // should trim right?
27975         if (node.nextSibling &&
27976             node.nextSibling.nodeType == 1 &&
27977             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27978         {
27979           // noop
27980             
27981         }  else {
27982             text = text.replace(/\s+$/,''); // trim right
27983         }
27984          
27985               
27986         
27987         
27988         
27989         if (text.length < 1) {
27990             return;
27991         }
27992         if (!text.match(/\n/)) {
27993             this.html.push(indentstr + text);
27994             return;
27995         }
27996         
27997         text = this.indentstr + text.replace(
27998             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27999         );
28000         // remoeve the last whitespace / line break.
28001         text = text.replace(/\s+$/,''); 
28002         
28003         this.html.push(text);
28004         
28005         // split and indent..
28006         
28007         
28008     },
28009     /**
28010      * Writes a cdata node such as <![CDATA[data]]>.
28011      *
28012      * @method cdata
28013      * @param {String} text String to write out inside the cdata.
28014      */
28015     cdata: function(text) {
28016         this.html.push('<![CDATA[', text, ']]>');
28017     },
28018     /**
28019     * Writes a comment node such as <!-- Comment -->.
28020     *
28021     * @method cdata
28022     * @param {String} text String to write out inside the comment.
28023     */
28024    comment: function(text) {
28025        this.html.push('<!--', text, '-->');
28026    },
28027     /**
28028      * Writes a PI node such as <?xml attr="value" ?>.
28029      *
28030      * @method pi
28031      * @param {String} name Name of the pi.
28032      * @param {String} text String to write out inside the pi.
28033      */
28034     pi: function(name, text) {
28035         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28036         this.indent != '' && this.html.push('\n');
28037     },
28038     /**
28039      * Writes a doctype node such as <!DOCTYPE data>.
28040      *
28041      * @method doctype
28042      * @param {String} text String to write out inside the doctype.
28043      */
28044     doctype: function(text) {
28045         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28046     },
28047     /**
28048      * Resets the internal buffer if one wants to reuse the writer.
28049      *
28050      * @method reset
28051      */
28052     reset: function() {
28053         this.html.length = 0;
28054         this.state = [];
28055         this.pushState({
28056             indentstr : '',
28057             in_pre : false, 
28058             in_inline : false
28059         })
28060     },
28061     /**
28062      * Returns the contents that got serialized.
28063      *
28064      * @method getContent
28065      * @return {String} HTML contents that got written down.
28066      */
28067     getContent: function() {
28068         return this.html.join('').replace(/\n$/, '');
28069     },
28070     
28071     pushState : function(cfg)
28072     {
28073         this.state.push(cfg);
28074         Roo.apply(this, cfg);
28075     },
28076     
28077     popState : function()
28078     {
28079         if (this.state.length < 1) {
28080             return; // nothing to push
28081         }
28082         var cfg = {
28083             in_pre: false,
28084             indentstr : ''
28085         };
28086         this.state.pop();
28087         if (this.state.length > 0) {
28088             cfg = this.state[this.state.length-1]; 
28089         }
28090         Roo.apply(this, cfg);
28091     },
28092     
28093     addLine: function()
28094     {
28095         if (this.html.length < 1) {
28096             return;
28097         }
28098         
28099         
28100         var value = this.html[this.html.length - 1];
28101         if (value.length > 0 && '\n' !== value) {
28102             this.html.push('\n');
28103         }
28104     }
28105     
28106     
28107 //'pre script noscript style textarea video audio iframe object code'
28108 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28109 // inline 
28110 };
28111
28112 Roo.htmleditor.TidyWriter.inline_elements = [
28113         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28114         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28115 ];
28116 Roo.htmleditor.TidyWriter.shortend_elements = [
28117     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28118     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28119 ];
28120
28121 Roo.htmleditor.TidyWriter.whitespace_elements = [
28122     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28123 ];/***
28124  * This is based loosely on tinymce 
28125  * @class Roo.htmleditor.TidyEntities
28126  * @static
28127  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28128  *
28129  * Not 100% sure this is actually used or needed.
28130  */
28131
28132 Roo.htmleditor.TidyEntities = {
28133     
28134     /**
28135      * initialize data..
28136      */
28137     init : function (){
28138      
28139         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28140        
28141     },
28142
28143
28144     buildEntitiesLookup: function(items, radix) {
28145         var i, chr, entity, lookup = {};
28146         if (!items) {
28147             return {};
28148         }
28149         items = typeof(items) == 'string' ? items.split(',') : items;
28150         radix = radix || 10;
28151         // Build entities lookup table
28152         for (i = 0; i < items.length; i += 2) {
28153             chr = String.fromCharCode(parseInt(items[i], radix));
28154             // Only add non base entities
28155             if (!this.baseEntities[chr]) {
28156                 entity = '&' + items[i + 1] + ';';
28157                 lookup[chr] = entity;
28158                 lookup[entity] = chr;
28159             }
28160         }
28161         return lookup;
28162         
28163     },
28164     
28165     asciiMap : {
28166             128: '€',
28167             130: '‚',
28168             131: 'ƒ',
28169             132: '„',
28170             133: '…',
28171             134: '†',
28172             135: '‡',
28173             136: 'ˆ',
28174             137: '‰',
28175             138: 'Š',
28176             139: '‹',
28177             140: 'Œ',
28178             142: 'Ž',
28179             145: '‘',
28180             146: '’',
28181             147: '“',
28182             148: '”',
28183             149: '•',
28184             150: '–',
28185             151: '—',
28186             152: '˜',
28187             153: '™',
28188             154: 'š',
28189             155: '›',
28190             156: 'œ',
28191             158: 'ž',
28192             159: 'Ÿ'
28193     },
28194     // Raw entities
28195     baseEntities : {
28196         '"': '&quot;',
28197         // Needs to be escaped since the YUI compressor would otherwise break the code
28198         '\'': '&#39;',
28199         '<': '&lt;',
28200         '>': '&gt;',
28201         '&': '&amp;',
28202         '`': '&#96;'
28203     },
28204     // Reverse lookup table for raw entities
28205     reverseEntities : {
28206         '&lt;': '<',
28207         '&gt;': '>',
28208         '&amp;': '&',
28209         '&quot;': '"',
28210         '&apos;': '\''
28211     },
28212     
28213     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28214     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28215     rawCharsRegExp : /[<>&\"\']/g,
28216     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28217     namedEntities  : false,
28218     namedEntitiesData : [ 
28219         '50',
28220         'nbsp',
28221         '51',
28222         'iexcl',
28223         '52',
28224         'cent',
28225         '53',
28226         'pound',
28227         '54',
28228         'curren',
28229         '55',
28230         'yen',
28231         '56',
28232         'brvbar',
28233         '57',
28234         'sect',
28235         '58',
28236         'uml',
28237         '59',
28238         'copy',
28239         '5a',
28240         'ordf',
28241         '5b',
28242         'laquo',
28243         '5c',
28244         'not',
28245         '5d',
28246         'shy',
28247         '5e',
28248         'reg',
28249         '5f',
28250         'macr',
28251         '5g',
28252         'deg',
28253         '5h',
28254         'plusmn',
28255         '5i',
28256         'sup2',
28257         '5j',
28258         'sup3',
28259         '5k',
28260         'acute',
28261         '5l',
28262         'micro',
28263         '5m',
28264         'para',
28265         '5n',
28266         'middot',
28267         '5o',
28268         'cedil',
28269         '5p',
28270         'sup1',
28271         '5q',
28272         'ordm',
28273         '5r',
28274         'raquo',
28275         '5s',
28276         'frac14',
28277         '5t',
28278         'frac12',
28279         '5u',
28280         'frac34',
28281         '5v',
28282         'iquest',
28283         '60',
28284         'Agrave',
28285         '61',
28286         'Aacute',
28287         '62',
28288         'Acirc',
28289         '63',
28290         'Atilde',
28291         '64',
28292         'Auml',
28293         '65',
28294         'Aring',
28295         '66',
28296         'AElig',
28297         '67',
28298         'Ccedil',
28299         '68',
28300         'Egrave',
28301         '69',
28302         'Eacute',
28303         '6a',
28304         'Ecirc',
28305         '6b',
28306         'Euml',
28307         '6c',
28308         'Igrave',
28309         '6d',
28310         'Iacute',
28311         '6e',
28312         'Icirc',
28313         '6f',
28314         'Iuml',
28315         '6g',
28316         'ETH',
28317         '6h',
28318         'Ntilde',
28319         '6i',
28320         'Ograve',
28321         '6j',
28322         'Oacute',
28323         '6k',
28324         'Ocirc',
28325         '6l',
28326         'Otilde',
28327         '6m',
28328         'Ouml',
28329         '6n',
28330         'times',
28331         '6o',
28332         'Oslash',
28333         '6p',
28334         'Ugrave',
28335         '6q',
28336         'Uacute',
28337         '6r',
28338         'Ucirc',
28339         '6s',
28340         'Uuml',
28341         '6t',
28342         'Yacute',
28343         '6u',
28344         'THORN',
28345         '6v',
28346         'szlig',
28347         '70',
28348         'agrave',
28349         '71',
28350         'aacute',
28351         '72',
28352         'acirc',
28353         '73',
28354         'atilde',
28355         '74',
28356         'auml',
28357         '75',
28358         'aring',
28359         '76',
28360         'aelig',
28361         '77',
28362         'ccedil',
28363         '78',
28364         'egrave',
28365         '79',
28366         'eacute',
28367         '7a',
28368         'ecirc',
28369         '7b',
28370         'euml',
28371         '7c',
28372         'igrave',
28373         '7d',
28374         'iacute',
28375         '7e',
28376         'icirc',
28377         '7f',
28378         'iuml',
28379         '7g',
28380         'eth',
28381         '7h',
28382         'ntilde',
28383         '7i',
28384         'ograve',
28385         '7j',
28386         'oacute',
28387         '7k',
28388         'ocirc',
28389         '7l',
28390         'otilde',
28391         '7m',
28392         'ouml',
28393         '7n',
28394         'divide',
28395         '7o',
28396         'oslash',
28397         '7p',
28398         'ugrave',
28399         '7q',
28400         'uacute',
28401         '7r',
28402         'ucirc',
28403         '7s',
28404         'uuml',
28405         '7t',
28406         'yacute',
28407         '7u',
28408         'thorn',
28409         '7v',
28410         'yuml',
28411         'ci',
28412         'fnof',
28413         'sh',
28414         'Alpha',
28415         'si',
28416         'Beta',
28417         'sj',
28418         'Gamma',
28419         'sk',
28420         'Delta',
28421         'sl',
28422         'Epsilon',
28423         'sm',
28424         'Zeta',
28425         'sn',
28426         'Eta',
28427         'so',
28428         'Theta',
28429         'sp',
28430         'Iota',
28431         'sq',
28432         'Kappa',
28433         'sr',
28434         'Lambda',
28435         'ss',
28436         'Mu',
28437         'st',
28438         'Nu',
28439         'su',
28440         'Xi',
28441         'sv',
28442         'Omicron',
28443         't0',
28444         'Pi',
28445         't1',
28446         'Rho',
28447         't3',
28448         'Sigma',
28449         't4',
28450         'Tau',
28451         't5',
28452         'Upsilon',
28453         't6',
28454         'Phi',
28455         't7',
28456         'Chi',
28457         't8',
28458         'Psi',
28459         't9',
28460         'Omega',
28461         'th',
28462         'alpha',
28463         'ti',
28464         'beta',
28465         'tj',
28466         'gamma',
28467         'tk',
28468         'delta',
28469         'tl',
28470         'epsilon',
28471         'tm',
28472         'zeta',
28473         'tn',
28474         'eta',
28475         'to',
28476         'theta',
28477         'tp',
28478         'iota',
28479         'tq',
28480         'kappa',
28481         'tr',
28482         'lambda',
28483         'ts',
28484         'mu',
28485         'tt',
28486         'nu',
28487         'tu',
28488         'xi',
28489         'tv',
28490         'omicron',
28491         'u0',
28492         'pi',
28493         'u1',
28494         'rho',
28495         'u2',
28496         'sigmaf',
28497         'u3',
28498         'sigma',
28499         'u4',
28500         'tau',
28501         'u5',
28502         'upsilon',
28503         'u6',
28504         'phi',
28505         'u7',
28506         'chi',
28507         'u8',
28508         'psi',
28509         'u9',
28510         'omega',
28511         'uh',
28512         'thetasym',
28513         'ui',
28514         'upsih',
28515         'um',
28516         'piv',
28517         '812',
28518         'bull',
28519         '816',
28520         'hellip',
28521         '81i',
28522         'prime',
28523         '81j',
28524         'Prime',
28525         '81u',
28526         'oline',
28527         '824',
28528         'frasl',
28529         '88o',
28530         'weierp',
28531         '88h',
28532         'image',
28533         '88s',
28534         'real',
28535         '892',
28536         'trade',
28537         '89l',
28538         'alefsym',
28539         '8cg',
28540         'larr',
28541         '8ch',
28542         'uarr',
28543         '8ci',
28544         'rarr',
28545         '8cj',
28546         'darr',
28547         '8ck',
28548         'harr',
28549         '8dl',
28550         'crarr',
28551         '8eg',
28552         'lArr',
28553         '8eh',
28554         'uArr',
28555         '8ei',
28556         'rArr',
28557         '8ej',
28558         'dArr',
28559         '8ek',
28560         'hArr',
28561         '8g0',
28562         'forall',
28563         '8g2',
28564         'part',
28565         '8g3',
28566         'exist',
28567         '8g5',
28568         'empty',
28569         '8g7',
28570         'nabla',
28571         '8g8',
28572         'isin',
28573         '8g9',
28574         'notin',
28575         '8gb',
28576         'ni',
28577         '8gf',
28578         'prod',
28579         '8gh',
28580         'sum',
28581         '8gi',
28582         'minus',
28583         '8gn',
28584         'lowast',
28585         '8gq',
28586         'radic',
28587         '8gt',
28588         'prop',
28589         '8gu',
28590         'infin',
28591         '8h0',
28592         'ang',
28593         '8h7',
28594         'and',
28595         '8h8',
28596         'or',
28597         '8h9',
28598         'cap',
28599         '8ha',
28600         'cup',
28601         '8hb',
28602         'int',
28603         '8hk',
28604         'there4',
28605         '8hs',
28606         'sim',
28607         '8i5',
28608         'cong',
28609         '8i8',
28610         'asymp',
28611         '8j0',
28612         'ne',
28613         '8j1',
28614         'equiv',
28615         '8j4',
28616         'le',
28617         '8j5',
28618         'ge',
28619         '8k2',
28620         'sub',
28621         '8k3',
28622         'sup',
28623         '8k4',
28624         'nsub',
28625         '8k6',
28626         'sube',
28627         '8k7',
28628         'supe',
28629         '8kl',
28630         'oplus',
28631         '8kn',
28632         'otimes',
28633         '8l5',
28634         'perp',
28635         '8m5',
28636         'sdot',
28637         '8o8',
28638         'lceil',
28639         '8o9',
28640         'rceil',
28641         '8oa',
28642         'lfloor',
28643         '8ob',
28644         'rfloor',
28645         '8p9',
28646         'lang',
28647         '8pa',
28648         'rang',
28649         '9ea',
28650         'loz',
28651         '9j0',
28652         'spades',
28653         '9j3',
28654         'clubs',
28655         '9j5',
28656         'hearts',
28657         '9j6',
28658         'diams',
28659         'ai',
28660         'OElig',
28661         'aj',
28662         'oelig',
28663         'b0',
28664         'Scaron',
28665         'b1',
28666         'scaron',
28667         'bo',
28668         'Yuml',
28669         'm6',
28670         'circ',
28671         'ms',
28672         'tilde',
28673         '802',
28674         'ensp',
28675         '803',
28676         'emsp',
28677         '809',
28678         'thinsp',
28679         '80c',
28680         'zwnj',
28681         '80d',
28682         'zwj',
28683         '80e',
28684         'lrm',
28685         '80f',
28686         'rlm',
28687         '80j',
28688         'ndash',
28689         '80k',
28690         'mdash',
28691         '80o',
28692         'lsquo',
28693         '80p',
28694         'rsquo',
28695         '80q',
28696         'sbquo',
28697         '80s',
28698         'ldquo',
28699         '80t',
28700         'rdquo',
28701         '80u',
28702         'bdquo',
28703         '810',
28704         'dagger',
28705         '811',
28706         'Dagger',
28707         '81g',
28708         'permil',
28709         '81p',
28710         'lsaquo',
28711         '81q',
28712         'rsaquo',
28713         '85c',
28714         'euro'
28715     ],
28716
28717          
28718     /**
28719      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28720      *
28721      * @method encodeRaw
28722      * @param {String} text Text to encode.
28723      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28724      * @return {String} Entity encoded text.
28725      */
28726     encodeRaw: function(text, attr)
28727     {
28728         var t = this;
28729         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28730             return t.baseEntities[chr] || chr;
28731         });
28732     },
28733     /**
28734      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28735      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28736      * and is exposed as the DOMUtils.encode function.
28737      *
28738      * @method encodeAllRaw
28739      * @param {String} text Text to encode.
28740      * @return {String} Entity encoded text.
28741      */
28742     encodeAllRaw: function(text) {
28743         var t = this;
28744         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28745             return t.baseEntities[chr] || chr;
28746         });
28747     },
28748     /**
28749      * Encodes the specified string using numeric entities. The core entities will be
28750      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28751      *
28752      * @method encodeNumeric
28753      * @param {String} text Text to encode.
28754      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28755      * @return {String} Entity encoded text.
28756      */
28757     encodeNumeric: function(text, attr) {
28758         var t = this;
28759         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28760             // Multi byte sequence convert it to a single entity
28761             if (chr.length > 1) {
28762                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28763             }
28764             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28765         });
28766     },
28767     /**
28768      * Encodes the specified string using named entities. The core entities will be encoded
28769      * as named ones but all non lower ascii characters will be encoded into named entities.
28770      *
28771      * @method encodeNamed
28772      * @param {String} text Text to encode.
28773      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28774      * @param {Object} entities Optional parameter with entities to use.
28775      * @return {String} Entity encoded text.
28776      */
28777     encodeNamed: function(text, attr, entities) {
28778         var t = this;
28779         entities = entities || this.namedEntities;
28780         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28781             return t.baseEntities[chr] || entities[chr] || chr;
28782         });
28783     },
28784     /**
28785      * Returns an encode function based on the name(s) and it's optional entities.
28786      *
28787      * @method getEncodeFunc
28788      * @param {String} name Comma separated list of encoders for example named,numeric.
28789      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28790      * @return {function} Encode function to be used.
28791      */
28792     getEncodeFunc: function(name, entities) {
28793         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28794         var t = this;
28795         function encodeNamedAndNumeric(text, attr) {
28796             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28797                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28798             });
28799         }
28800
28801         function encodeCustomNamed(text, attr) {
28802             return t.encodeNamed(text, attr, entities);
28803         }
28804         // Replace + with , to be compatible with previous TinyMCE versions
28805         name = this.makeMap(name.replace(/\+/g, ','));
28806         // Named and numeric encoder
28807         if (name.named && name.numeric) {
28808             return this.encodeNamedAndNumeric;
28809         }
28810         // Named encoder
28811         if (name.named) {
28812             // Custom names
28813             if (entities) {
28814                 return encodeCustomNamed;
28815             }
28816             return this.encodeNamed;
28817         }
28818         // Numeric
28819         if (name.numeric) {
28820             return this.encodeNumeric;
28821         }
28822         // Raw encoder
28823         return this.encodeRaw;
28824     },
28825     /**
28826      * Decodes the specified string, this will replace entities with raw UTF characters.
28827      *
28828      * @method decode
28829      * @param {String} text Text to entity decode.
28830      * @return {String} Entity decoded string.
28831      */
28832     decode: function(text)
28833     {
28834         var  t = this;
28835         return text.replace(this.entityRegExp, function(all, numeric) {
28836             if (numeric) {
28837                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28838                 // Support upper UTF
28839                 if (numeric > 65535) {
28840                     numeric -= 65536;
28841                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28842                 }
28843                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28844             }
28845             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28846         });
28847     },
28848     nativeDecode : function (text) {
28849         return text;
28850     },
28851     makeMap : function (items, delim, map) {
28852                 var i;
28853                 items = items || [];
28854                 delim = delim || ',';
28855                 if (typeof items == "string") {
28856                         items = items.split(delim);
28857                 }
28858                 map = map || {};
28859                 i = items.length;
28860                 while (i--) {
28861                         map[items[i]] = {};
28862                 }
28863                 return map;
28864         }
28865 };
28866     
28867     
28868     
28869 Roo.htmleditor.TidyEntities.init();
28870 /**
28871  * @class Roo.htmleditor.KeyEnter
28872  * Handle Enter press..
28873  * @cfg {Roo.HtmlEditorCore} core the editor.
28874  * @constructor
28875  * Create a new Filter.
28876  * @param {Object} config Configuration options
28877  */
28878
28879
28880
28881
28882
28883 Roo.htmleditor.KeyEnter = function(cfg) {
28884     Roo.apply(this, cfg);
28885     // this does not actually call walk as it's really just a abstract class
28886  
28887     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28888 }
28889
28890 //Roo.htmleditor.KeyEnter.i = 0;
28891
28892
28893 Roo.htmleditor.KeyEnter.prototype = {
28894     
28895     core : false,
28896     
28897     keypress : function(e)
28898     {
28899         if (e.charCode != 13 && e.charCode != 10) {
28900             Roo.log([e.charCode,e]);
28901             return true;
28902         }
28903         e.preventDefault();
28904         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28905         var doc = this.core.doc;
28906           //add a new line
28907        
28908     
28909         var sel = this.core.getSelection();
28910         var range = sel.getRangeAt(0);
28911         var n = range.commonAncestorContainer;
28912         var pc = range.closest([ 'ol', 'ul']);
28913         var pli = range.closest('li');
28914         if (!pc || e.ctrlKey) {
28915             // on it list, or ctrl pressed.
28916             if (!e.ctrlKey) {
28917                 sel.insertNode('br', 'after'); 
28918             } else {
28919                 // only do this if we have ctrl key..
28920                 var br = doc.createElement('br');
28921                 br.className = 'clear';
28922                 br.setAttribute('style', 'clear: both');
28923                 sel.insertNode(br, 'after'); 
28924             }
28925             
28926          
28927             this.core.undoManager.addEvent();
28928             this.core.fireEditorEvent(e);
28929             return false;
28930         }
28931         
28932         // deal with <li> insetion
28933         if (pli.innerText.trim() == '' &&
28934             pli.previousSibling &&
28935             pli.previousSibling.nodeName == 'LI' &&
28936             pli.previousSibling.innerText.trim() ==  '') {
28937             pli.parentNode.removeChild(pli.previousSibling);
28938             sel.cursorAfter(pc);
28939             this.core.undoManager.addEvent();
28940             this.core.fireEditorEvent(e);
28941             return false;
28942         }
28943     
28944         var li = doc.createElement('LI');
28945         li.innerHTML = '&nbsp;';
28946         if (!pli || !pli.firstSibling) {
28947             pc.appendChild(li);
28948         } else {
28949             pli.parentNode.insertBefore(li, pli.firstSibling);
28950         }
28951         sel.cursorText (li.firstChild);
28952       
28953         this.core.undoManager.addEvent();
28954         this.core.fireEditorEvent(e);
28955
28956         return false;
28957         
28958     
28959         
28960         
28961          
28962     }
28963 };
28964      
28965 /**
28966  * @class Roo.htmleditor.Block
28967  * Base class for html editor blocks - do not use it directly .. extend it..
28968  * @cfg {DomElement} node The node to apply stuff to.
28969  * @cfg {String} friendly_name the name that appears in the context bar about this block
28970  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28971  
28972  * @constructor
28973  * Create a new Filter.
28974  * @param {Object} config Configuration options
28975  */
28976
28977 Roo.htmleditor.Block  = function(cfg)
28978 {
28979     // do nothing .. should not be called really.
28980 }
28981 /**
28982  * factory method to get the block from an element (using cache if necessary)
28983  * @static
28984  * @param {HtmlElement} the dom element
28985  */
28986 Roo.htmleditor.Block.factory = function(node)
28987 {
28988     var cc = Roo.htmleditor.Block.cache;
28989     var id = Roo.get(node).id;
28990     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28991         Roo.htmleditor.Block.cache[id].readElement(node);
28992         return Roo.htmleditor.Block.cache[id];
28993     }
28994     var db  = node.getAttribute('data-block');
28995     if (!db) {
28996         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28997     }
28998     var cls = Roo.htmleditor['Block' + db];
28999     if (typeof(cls) == 'undefined') {
29000         //Roo.log(node.getAttribute('data-block'));
29001         Roo.log("OOps missing block : " + 'Block' + db);
29002         return false;
29003     }
29004     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29005     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29006 };
29007
29008 /**
29009  * initalize all Elements from content that are 'blockable'
29010  * @static
29011  * @param the body element
29012  */
29013 Roo.htmleditor.Block.initAll = function(body, type)
29014 {
29015     if (typeof(type) == 'undefined') {
29016         var ia = Roo.htmleditor.Block.initAll;
29017         ia(body,'table');
29018         ia(body,'td');
29019         ia(body,'figure');
29020         return;
29021     }
29022     Roo.each(Roo.get(body).query(type), function(e) {
29023         Roo.htmleditor.Block.factory(e);    
29024     },this);
29025 };
29026 // question goes here... do we need to clear out this cache sometimes?
29027 // or show we make it relivant to the htmleditor.
29028 Roo.htmleditor.Block.cache = {};
29029
29030 Roo.htmleditor.Block.prototype = {
29031     
29032     node : false,
29033     
29034      // used by context menu
29035     friendly_name : 'Based Block',
29036     
29037     // text for button to delete this element
29038     deleteTitle : false,
29039     
29040     context : false,
29041     /**
29042      * Update a node with values from this object
29043      * @param {DomElement} node
29044      */
29045     updateElement : function(node)
29046     {
29047         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29048     },
29049      /**
29050      * convert to plain HTML for calling insertAtCursor..
29051      */
29052     toHTML : function()
29053     {
29054         return Roo.DomHelper.markup(this.toObject());
29055     },
29056     /**
29057      * used by readEleemnt to extract data from a node
29058      * may need improving as it's pretty basic
29059      
29060      * @param {DomElement} node
29061      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29062      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29063      * @param {String} style the style property - eg. text-align
29064      */
29065     getVal : function(node, tag, attr, style)
29066     {
29067         var n = node;
29068         if (tag !== true && n.tagName != tag.toUpperCase()) {
29069             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29070             // but kiss for now.
29071             n = node.getElementsByTagName(tag).item(0);
29072         }
29073         if (!n) {
29074             return '';
29075         }
29076         if (attr === false) {
29077             return n;
29078         }
29079         if (attr == 'html') {
29080             return n.innerHTML;
29081         }
29082         if (attr == 'style') {
29083             return n.style[style]; 
29084         }
29085         
29086         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29087             
29088     },
29089     /**
29090      * create a DomHelper friendly object - for use with 
29091      * Roo.DomHelper.markup / overwrite / etc..
29092      * (override this)
29093      */
29094     toObject : function()
29095     {
29096         return {};
29097     },
29098       /**
29099      * Read a node that has a 'data-block' property - and extract the values from it.
29100      * @param {DomElement} node - the node
29101      */
29102     readElement : function(node)
29103     {
29104         
29105     } 
29106     
29107     
29108 };
29109
29110  
29111
29112 /**
29113  * @class Roo.htmleditor.BlockFigure
29114  * Block that has an image and a figcaption
29115  * @cfg {String} image_src the url for the image
29116  * @cfg {String} align (left|right) alignment for the block default left
29117  * @cfg {String} caption the text to appear below  (and in the alt tag)
29118  * @cfg {String} caption_display (block|none) display or not the caption
29119  * @cfg {String|number} image_width the width of the image number or %?
29120  * @cfg {String|number} image_height the height of the image number or %?
29121  * 
29122  * @constructor
29123  * Create a new Filter.
29124  * @param {Object} config Configuration options
29125  */
29126
29127 Roo.htmleditor.BlockFigure = function(cfg)
29128 {
29129     if (cfg.node) {
29130         this.readElement(cfg.node);
29131         this.updateElement(cfg.node);
29132     }
29133     Roo.apply(this, cfg);
29134 }
29135 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29136  
29137     
29138     // setable values.
29139     image_src: '',
29140     align: 'center',
29141     caption : '',
29142     caption_display : 'block',
29143     width : '100%',
29144     cls : '',
29145     href: '',
29146     video_url : '',
29147     
29148     // margin: '2%', not used
29149     
29150     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29151
29152     
29153     // used by context menu
29154     friendly_name : 'Image with caption',
29155     deleteTitle : "Delete Image and Caption",
29156     
29157     contextMenu : function(toolbar)
29158     {
29159         
29160         var block = function() {
29161             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29162         };
29163         
29164         
29165         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29166         
29167         var syncValue = toolbar.editorcore.syncValue;
29168         
29169         var fields = {};
29170         
29171         return [
29172              {
29173                 xtype : 'TextItem',
29174                 text : "Source: ",
29175                 xns : rooui.Toolbar  //Boostrap?
29176             },
29177             {
29178                 xtype : 'Button',
29179                 text: 'Change Image URL',
29180                  
29181                 listeners : {
29182                     click: function (btn, state)
29183                     {
29184                         var b = block();
29185                         
29186                         Roo.MessageBox.show({
29187                             title : "Image Source URL",
29188                             msg : "Enter the url for the image",
29189                             buttons: Roo.MessageBox.OKCANCEL,
29190                             fn: function(btn, val){
29191                                 if (btn != 'ok') {
29192                                     return;
29193                                 }
29194                                 b.image_src = val;
29195                                 b.updateElement();
29196                                 syncValue();
29197                                 toolbar.editorcore.onEditorEvent();
29198                             },
29199                             minWidth:250,
29200                             prompt:true,
29201                             //multiline: multiline,
29202                             modal : true,
29203                             value : b.image_src
29204                         });
29205                     }
29206                 },
29207                 xns : rooui.Toolbar
29208             },
29209          
29210             {
29211                 xtype : 'Button',
29212                 text: 'Change Link URL',
29213                  
29214                 listeners : {
29215                     click: function (btn, state)
29216                     {
29217                         var b = block();
29218                         
29219                         Roo.MessageBox.show({
29220                             title : "Link URL",
29221                             msg : "Enter the url for the link - leave blank to have no link",
29222                             buttons: Roo.MessageBox.OKCANCEL,
29223                             fn: function(btn, val){
29224                                 if (btn != 'ok') {
29225                                     return;
29226                                 }
29227                                 b.href = val;
29228                                 b.updateElement();
29229                                 syncValue();
29230                                 toolbar.editorcore.onEditorEvent();
29231                             },
29232                             minWidth:250,
29233                             prompt:true,
29234                             //multiline: multiline,
29235                             modal : true,
29236                             value : b.href
29237                         });
29238                     }
29239                 },
29240                 xns : rooui.Toolbar
29241             },
29242             {
29243                 xtype : 'Button',
29244                 text: 'Show Video URL',
29245                  
29246                 listeners : {
29247                     click: function (btn, state)
29248                     {
29249                         Roo.MessageBox.alert("Video URL",
29250                             block().video_url == '' ? 'This image is not linked ot a video' :
29251                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29252                     }
29253                 },
29254                 xns : rooui.Toolbar
29255             },
29256             
29257             
29258             {
29259                 xtype : 'TextItem',
29260                 text : "Width: ",
29261                 xns : rooui.Toolbar  //Boostrap?
29262             },
29263             {
29264                 xtype : 'ComboBox',
29265                 allowBlank : false,
29266                 displayField : 'val',
29267                 editable : true,
29268                 listWidth : 100,
29269                 triggerAction : 'all',
29270                 typeAhead : true,
29271                 valueField : 'val',
29272                 width : 70,
29273                 name : 'width',
29274                 listeners : {
29275                     select : function (combo, r, index)
29276                     {
29277                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29278                         var b = block();
29279                         b.width = r.get('val');
29280                         b.updateElement();
29281                         syncValue();
29282                         toolbar.editorcore.onEditorEvent();
29283                     }
29284                 },
29285                 xns : rooui.form,
29286                 store : {
29287                     xtype : 'SimpleStore',
29288                     data : [
29289                         ['100%'],
29290                         ['80%'],
29291                         ['50%'],
29292                         ['20%'],
29293                         ['10%']
29294                     ],
29295                     fields : [ 'val'],
29296                     xns : Roo.data
29297                 }
29298             },
29299             {
29300                 xtype : 'TextItem',
29301                 text : "Align: ",
29302                 xns : rooui.Toolbar  //Boostrap?
29303             },
29304             {
29305                 xtype : 'ComboBox',
29306                 allowBlank : false,
29307                 displayField : 'val',
29308                 editable : true,
29309                 listWidth : 100,
29310                 triggerAction : 'all',
29311                 typeAhead : true,
29312                 valueField : 'val',
29313                 width : 70,
29314                 name : 'align',
29315                 listeners : {
29316                     select : function (combo, r, index)
29317                     {
29318                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29319                         var b = block();
29320                         b.align = r.get('val');
29321                         b.updateElement();
29322                         syncValue();
29323                         toolbar.editorcore.onEditorEvent();
29324                     }
29325                 },
29326                 xns : rooui.form,
29327                 store : {
29328                     xtype : 'SimpleStore',
29329                     data : [
29330                         ['left'],
29331                         ['right'],
29332                         ['center']
29333                     ],
29334                     fields : [ 'val'],
29335                     xns : Roo.data
29336                 }
29337             },
29338             
29339               
29340             {
29341                 xtype : 'Button',
29342                 text: 'Hide Caption',
29343                 name : 'caption_display',
29344                 pressed : false,
29345                 enableToggle : true,
29346                 setValue : function(v) {
29347                     // this trigger toggle.
29348                      
29349                     this.setText(v ? "Hide Caption" : "Show Caption");
29350                     this.setPressed(v != 'block');
29351                 },
29352                 listeners : {
29353                     toggle: function (btn, state)
29354                     {
29355                         var b  = block();
29356                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29357                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29358                         b.updateElement();
29359                         syncValue();
29360                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29361                         toolbar.editorcore.onEditorEvent();
29362                     }
29363                 },
29364                 xns : rooui.Toolbar
29365             }
29366         ];
29367         
29368     },
29369     /**
29370      * create a DomHelper friendly object - for use with
29371      * Roo.DomHelper.markup / overwrite / etc..
29372      */
29373     toObject : function()
29374     {
29375         var d = document.createElement('div');
29376         d.innerHTML = this.caption;
29377         
29378         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29379         
29380         var iw = this.align == 'center' ? this.width : '100%';
29381         var img =   {
29382             tag : 'img',
29383             contenteditable : 'false',
29384             src : this.image_src,
29385             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29386             style: {
29387                 width : iw,
29388                 maxWidth : iw + ' !important', // this is not getting rendered?
29389                 margin : m  
29390                 
29391             }
29392         };
29393         /*
29394         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29395                     '<a href="{2}">' + 
29396                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29397                     '</a>' + 
29398                 '</div>',
29399         */
29400                 
29401         if (this.href.length > 0) {
29402             img = {
29403                 tag : 'a',
29404                 href: this.href,
29405                 contenteditable : 'true',
29406                 cn : [
29407                     img
29408                 ]
29409             };
29410         }
29411         
29412         
29413         if (this.video_url.length > 0) {
29414             img = {
29415                 tag : 'div',
29416                 cls : this.cls,
29417                 frameborder : 0,
29418                 allowfullscreen : true,
29419                 width : 420,  // these are for video tricks - that we replace the outer
29420                 height : 315,
29421                 src : this.video_url,
29422                 cn : [
29423                     img
29424                 ]
29425             };
29426         }
29427         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29428         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29429         
29430   
29431         var ret =   {
29432             tag: 'figure',
29433             'data-block' : 'Figure',
29434             'data-width' : this.width,
29435             'data-caption' : this.caption, 
29436             contenteditable : 'false',
29437             
29438             style : {
29439                 display: 'block',
29440                 float :  this.align ,
29441                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29442                 width : this.align == 'center' ? '100%' : this.width,
29443                 margin:  '0px',
29444                 padding: this.align == 'center' ? '0' : '0 10px' ,
29445                 textAlign : this.align   // seems to work for email..
29446                 
29447             },
29448            
29449             
29450             align : this.align,
29451             cn : [
29452                 img,
29453               
29454                 {
29455                     tag: 'figcaption',
29456                     'data-display' : this.caption_display,
29457                     style : {
29458                         textAlign : 'left',
29459                         fontSize : '16px',
29460                         lineHeight : '24px',
29461                         display : this.caption_display,
29462                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29463                         margin: m,
29464                         width: this.align == 'center' ?  this.width : '100%' 
29465                     
29466                          
29467                     },
29468                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29469                     cn : [
29470                         {
29471                             tag: 'div',
29472                             style  : {
29473                                 marginTop : '16px',
29474                                 textAlign : 'left'
29475                             },
29476                             align: 'left',
29477                             cn : [
29478                                 {
29479                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29480                                     tag : 'i',
29481                                     contenteditable : true,
29482                                     html : captionhtml
29483                                 }
29484                                 
29485                             ]
29486                         }
29487                         
29488                     ]
29489                     
29490                 }
29491             ]
29492         };
29493         return ret;
29494          
29495     },
29496     
29497     readElement : function(node)
29498     {
29499         // this should not really come from the link...
29500         this.video_url = this.getVal(node, 'div', 'src');
29501         this.cls = this.getVal(node, 'div', 'class');
29502         this.href = this.getVal(node, 'a', 'href');
29503         
29504         
29505         this.image_src = this.getVal(node, 'img', 'src');
29506          
29507         this.align = this.getVal(node, 'figure', 'align');
29508         
29509         /// not really used - as hidden captions do not store the content here..
29510         var figcaption = this.getVal(node, 'figcaption', false);
29511         if (figcaption !== '') {
29512             this.caption = this.getVal(figcaption, 'i', 'html');
29513         }
29514         
29515
29516         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29517         var dc = this.getVal(node, true, 'data-caption');
29518         if (dc && dc.length) {
29519             this.caption = dc;
29520         }
29521         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29522         this.width = this.getVal(node, true, 'data-width');
29523         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29524         
29525     },
29526     removeNode : function()
29527     {
29528         return this.node;
29529     }
29530     
29531   
29532    
29533      
29534     
29535     
29536     
29537     
29538 })
29539
29540  
29541
29542 /**
29543  * @class Roo.htmleditor.BlockTable
29544  * Block that manages a table
29545  * 
29546  * @constructor
29547  * Create a new Filter.
29548  * @param {Object} config Configuration options
29549  */
29550
29551 Roo.htmleditor.BlockTable = function(cfg)
29552 {
29553     if (cfg.node) {
29554         this.readElement(cfg.node);
29555         this.updateElement(cfg.node);
29556     }
29557     Roo.apply(this, cfg);
29558     if (!cfg.node) {
29559         this.rows = [];
29560         for(var r = 0; r < this.no_row; r++) {
29561             this.rows[r] = [];
29562             for(var c = 0; c < this.no_col; c++) {
29563                 this.rows[r][c] = this.emptyCell();
29564             }
29565         }
29566     }
29567     
29568     
29569 }
29570 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29571  
29572     rows : false,
29573     no_col : 1,
29574     no_row : 1,
29575     
29576     
29577     width: '100%',
29578     
29579     // used by context menu
29580     friendly_name : 'Table',
29581     deleteTitle : 'Delete Table',
29582     // context menu is drawn once..
29583     
29584     contextMenu : function(toolbar)
29585     {
29586         
29587         var block = function() {
29588             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29589         };
29590         
29591         
29592         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29593         
29594         var syncValue = toolbar.editorcore.syncValue;
29595         
29596         var fields = {};
29597         
29598         return [
29599             {
29600                 xtype : 'TextItem',
29601                 text : "Width: ",
29602                 xns : rooui.Toolbar  //Boostrap?
29603             },
29604             {
29605                 xtype : 'ComboBox',
29606                 allowBlank : false,
29607                 displayField : 'val',
29608                 editable : true,
29609                 listWidth : 100,
29610                 triggerAction : 'all',
29611                 typeAhead : true,
29612                 valueField : 'val',
29613                 width : 100,
29614                 name : 'width',
29615                 listeners : {
29616                     select : function (combo, r, index)
29617                     {
29618                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29619                         var b = block();
29620                         b.width = r.get('val');
29621                         b.updateElement();
29622                         syncValue();
29623                         toolbar.editorcore.onEditorEvent();
29624                     }
29625                 },
29626                 xns : rooui.form,
29627                 store : {
29628                     xtype : 'SimpleStore',
29629                     data : [
29630                         ['100%'],
29631                         ['auto']
29632                     ],
29633                     fields : [ 'val'],
29634                     xns : Roo.data
29635                 }
29636             },
29637             // -------- Cols
29638             
29639             {
29640                 xtype : 'TextItem',
29641                 text : "Columns: ",
29642                 xns : rooui.Toolbar  //Boostrap?
29643             },
29644          
29645             {
29646                 xtype : 'Button',
29647                 text: '-',
29648                 listeners : {
29649                     click : function (_self, e)
29650                     {
29651                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652                         block().removeColumn();
29653                         syncValue();
29654                         toolbar.editorcore.onEditorEvent();
29655                     }
29656                 },
29657                 xns : rooui.Toolbar
29658             },
29659             {
29660                 xtype : 'Button',
29661                 text: '+',
29662                 listeners : {
29663                     click : function (_self, e)
29664                     {
29665                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29666                         block().addColumn();
29667                         syncValue();
29668                         toolbar.editorcore.onEditorEvent();
29669                     }
29670                 },
29671                 xns : rooui.Toolbar
29672             },
29673             // -------- ROWS
29674             {
29675                 xtype : 'TextItem',
29676                 text : "Rows: ",
29677                 xns : rooui.Toolbar  //Boostrap?
29678             },
29679          
29680             {
29681                 xtype : 'Button',
29682                 text: '-',
29683                 listeners : {
29684                     click : function (_self, e)
29685                     {
29686                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29687                         block().removeRow();
29688                         syncValue();
29689                         toolbar.editorcore.onEditorEvent();
29690                     }
29691                 },
29692                 xns : rooui.Toolbar
29693             },
29694             {
29695                 xtype : 'Button',
29696                 text: '+',
29697                 listeners : {
29698                     click : function (_self, e)
29699                     {
29700                         block().addRow();
29701                         syncValue();
29702                         toolbar.editorcore.onEditorEvent();
29703                     }
29704                 },
29705                 xns : rooui.Toolbar
29706             },
29707             // -------- ROWS
29708             {
29709                 xtype : 'Button',
29710                 text: 'Reset Column Widths',
29711                 listeners : {
29712                     
29713                     click : function (_self, e)
29714                     {
29715                         block().resetWidths();
29716                         syncValue();
29717                         toolbar.editorcore.onEditorEvent();
29718                     }
29719                 },
29720                 xns : rooui.Toolbar
29721             } 
29722             
29723             
29724             
29725         ];
29726         
29727     },
29728     
29729     
29730   /**
29731      * create a DomHelper friendly object - for use with
29732      * Roo.DomHelper.markup / overwrite / etc..
29733      * ?? should it be called with option to hide all editing features?
29734      */
29735     toObject : function()
29736     {
29737         
29738         var ret = {
29739             tag : 'table',
29740             contenteditable : 'false', // this stops cell selection from picking the table.
29741             'data-block' : 'Table',
29742             style : {
29743                 width:  this.width,
29744                 border : 'solid 1px #000', // ??? hard coded?
29745                 'border-collapse' : 'collapse' 
29746             },
29747             cn : [
29748                 { tag : 'tbody' , cn : [] }
29749             ]
29750         };
29751         
29752         // do we have a head = not really 
29753         var ncols = 0;
29754         Roo.each(this.rows, function( row ) {
29755             var tr = {
29756                 tag: 'tr',
29757                 style : {
29758                     margin: '6px',
29759                     border : 'solid 1px #000',
29760                     textAlign : 'left' 
29761                 },
29762                 cn : [ ]
29763             };
29764             
29765             ret.cn[0].cn.push(tr);
29766             // does the row have any properties? ?? height?
29767             var nc = 0;
29768             Roo.each(row, function( cell ) {
29769                 
29770                 var td = {
29771                     tag : 'td',
29772                     contenteditable :  'true',
29773                     'data-block' : 'Td',
29774                     html : cell.html,
29775                     style : cell.style
29776                 };
29777                 if (cell.colspan > 1) {
29778                     td.colspan = cell.colspan ;
29779                     nc += cell.colspan;
29780                 } else {
29781                     nc++;
29782                 }
29783                 if (cell.rowspan > 1) {
29784                     td.rowspan = cell.rowspan ;
29785                 }
29786                 
29787                 
29788                 // widths ?
29789                 tr.cn.push(td);
29790                     
29791                 
29792             }, this);
29793             ncols = Math.max(nc, ncols);
29794             
29795             
29796         }, this);
29797         // add the header row..
29798         
29799         ncols++;
29800          
29801         
29802         return ret;
29803          
29804     },
29805     
29806     readElement : function(node)
29807     {
29808         node  = node ? node : this.node ;
29809         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29810         
29811         this.rows = [];
29812         this.no_row = 0;
29813         var trs = Array.from(node.rows);
29814         trs.forEach(function(tr) {
29815             var row =  [];
29816             this.rows.push(row);
29817             
29818             this.no_row++;
29819             var no_column = 0;
29820             Array.from(tr.cells).forEach(function(td) {
29821                 
29822                 var add = {
29823                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29824                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29825                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29826                     html : td.innerHTML
29827                 };
29828                 no_column += add.colspan;
29829                      
29830                 
29831                 row.push(add);
29832                 
29833                 
29834             },this);
29835             this.no_col = Math.max(this.no_col, no_column);
29836             
29837             
29838         },this);
29839         
29840         
29841     },
29842     normalizeRows: function()
29843     {
29844         var ret= [];
29845         var rid = -1;
29846         this.rows.forEach(function(row) {
29847             rid++;
29848             ret[rid] = [];
29849             row = this.normalizeRow(row);
29850             var cid = 0;
29851             row.forEach(function(c) {
29852                 while (typeof(ret[rid][cid]) != 'undefined') {
29853                     cid++;
29854                 }
29855                 if (typeof(ret[rid]) == 'undefined') {
29856                     ret[rid] = [];
29857                 }
29858                 ret[rid][cid] = c;
29859                 c.row = rid;
29860                 c.col = cid;
29861                 if (c.rowspan < 2) {
29862                     return;
29863                 }
29864                 
29865                 for(var i = 1 ;i < c.rowspan; i++) {
29866                     if (typeof(ret[rid+i]) == 'undefined') {
29867                         ret[rid+i] = [];
29868                     }
29869                     ret[rid+i][cid] = c;
29870                 }
29871             });
29872         }, this);
29873         return ret;
29874     
29875     },
29876     
29877     normalizeRow: function(row)
29878     {
29879         var ret= [];
29880         row.forEach(function(c) {
29881             if (c.colspan < 2) {
29882                 ret.push(c);
29883                 return;
29884             }
29885             for(var i =0 ;i < c.colspan; i++) {
29886                 ret.push(c);
29887             }
29888         });
29889         return ret;
29890     
29891     },
29892     
29893     deleteColumn : function(sel)
29894     {
29895         if (!sel || sel.type != 'col') {
29896             return;
29897         }
29898         if (this.no_col < 2) {
29899             return;
29900         }
29901         
29902         this.rows.forEach(function(row) {
29903             var cols = this.normalizeRow(row);
29904             var col = cols[sel.col];
29905             if (col.colspan > 1) {
29906                 col.colspan --;
29907             } else {
29908                 row.remove(col);
29909             }
29910             
29911         }, this);
29912         this.no_col--;
29913         
29914     },
29915     removeColumn : function()
29916     {
29917         this.deleteColumn({
29918             type: 'col',
29919             col : this.no_col-1
29920         });
29921         this.updateElement();
29922     },
29923     
29924      
29925     addColumn : function()
29926     {
29927         
29928         this.rows.forEach(function(row) {
29929             row.push(this.emptyCell());
29930            
29931         }, this);
29932         this.updateElement();
29933     },
29934     
29935     deleteRow : function(sel)
29936     {
29937         if (!sel || sel.type != 'row') {
29938             return;
29939         }
29940         
29941         if (this.no_row < 2) {
29942             return;
29943         }
29944         
29945         var rows = this.normalizeRows();
29946         
29947         
29948         rows[sel.row].forEach(function(col) {
29949             if (col.rowspan > 1) {
29950                 col.rowspan--;
29951             } else {
29952                 col.remove = 1; // flage it as removed.
29953             }
29954             
29955         }, this);
29956         var newrows = [];
29957         this.rows.forEach(function(row) {
29958             newrow = [];
29959             row.forEach(function(c) {
29960                 if (typeof(c.remove) == 'undefined') {
29961                     newrow.push(c);
29962                 }
29963                 
29964             });
29965             if (newrow.length > 0) {
29966                 newrows.push(row);
29967             }
29968         });
29969         this.rows =  newrows;
29970         
29971         
29972         
29973         this.no_row--;
29974         this.updateElement();
29975         
29976     },
29977     removeRow : function()
29978     {
29979         this.deleteRow({
29980             type: 'row',
29981             row : this.no_row-1
29982         });
29983         
29984     },
29985     
29986      
29987     addRow : function()
29988     {
29989         
29990         var row = [];
29991         for (var i = 0; i < this.no_col; i++ ) {
29992             
29993             row.push(this.emptyCell());
29994            
29995         }
29996         this.rows.push(row);
29997         this.updateElement();
29998         
29999     },
30000      
30001     // the default cell object... at present...
30002     emptyCell : function() {
30003         return (new Roo.htmleditor.BlockTd({})).toObject();
30004         
30005      
30006     },
30007     
30008     removeNode : function()
30009     {
30010         return this.node;
30011     },
30012     
30013     
30014     
30015     resetWidths : function()
30016     {
30017         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30018             var nn = Roo.htmleditor.Block.factory(n);
30019             nn.width = '';
30020             nn.updateElement(n);
30021         });
30022     }
30023     
30024     
30025     
30026     
30027 })
30028
30029 /**
30030  *
30031  * editing a TD?
30032  *
30033  * since selections really work on the table cell, then editing really should work from there
30034  *
30035  * The original plan was to support merging etc... - but that may not be needed yet..
30036  *
30037  * So this simple version will support:
30038  *   add/remove cols
30039  *   adjust the width +/-
30040  *   reset the width...
30041  *   
30042  *
30043  */
30044
30045
30046  
30047
30048 /**
30049  * @class Roo.htmleditor.BlockTable
30050  * Block that manages a table
30051  * 
30052  * @constructor
30053  * Create a new Filter.
30054  * @param {Object} config Configuration options
30055  */
30056
30057 Roo.htmleditor.BlockTd = function(cfg)
30058 {
30059     if (cfg.node) {
30060         this.readElement(cfg.node);
30061         this.updateElement(cfg.node);
30062     }
30063     Roo.apply(this, cfg);
30064      
30065     
30066     
30067 }
30068 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30069  
30070     node : false,
30071     
30072     width: '',
30073     textAlign : 'left',
30074     valign : 'top',
30075     
30076     colspan : 1,
30077     rowspan : 1,
30078     
30079     
30080     // used by context menu
30081     friendly_name : 'Table Cell',
30082     deleteTitle : false, // use our customer delete
30083     
30084     // context menu is drawn once..
30085     
30086     contextMenu : function(toolbar)
30087     {
30088         
30089         var cell = function() {
30090             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30091         };
30092         
30093         var table = function() {
30094             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30095         };
30096         
30097         var lr = false;
30098         var saveSel = function()
30099         {
30100             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30101         }
30102         var restoreSel = function()
30103         {
30104             if (lr) {
30105                 (function() {
30106                     toolbar.editorcore.focus();
30107                     var cr = toolbar.editorcore.getSelection();
30108                     cr.removeAllRanges();
30109                     cr.addRange(lr);
30110                     toolbar.editorcore.onEditorEvent();
30111                 }).defer(10, this);
30112                 
30113                 
30114             }
30115         }
30116         
30117         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30118         
30119         var syncValue = toolbar.editorcore.syncValue;
30120         
30121         var fields = {};
30122         
30123         return [
30124             {
30125                 xtype : 'Button',
30126                 text : 'Edit Table',
30127                 listeners : {
30128                     click : function() {
30129                         var t = toolbar.tb.selectedNode.closest('table');
30130                         toolbar.editorcore.selectNode(t);
30131                         toolbar.editorcore.onEditorEvent();                        
30132                     }
30133                 }
30134                 
30135             },
30136               
30137            
30138              
30139             {
30140                 xtype : 'TextItem',
30141                 text : "Column Width: ",
30142                  xns : rooui.Toolbar 
30143                
30144             },
30145             {
30146                 xtype : 'Button',
30147                 text: '-',
30148                 listeners : {
30149                     click : function (_self, e)
30150                     {
30151                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30152                         cell().shrinkColumn();
30153                         syncValue();
30154                          toolbar.editorcore.onEditorEvent();
30155                     }
30156                 },
30157                 xns : rooui.Toolbar
30158             },
30159             {
30160                 xtype : 'Button',
30161                 text: '+',
30162                 listeners : {
30163                     click : function (_self, e)
30164                     {
30165                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30166                         cell().growColumn();
30167                         syncValue();
30168                         toolbar.editorcore.onEditorEvent();
30169                     }
30170                 },
30171                 xns : rooui.Toolbar
30172             },
30173             
30174             {
30175                 xtype : 'TextItem',
30176                 text : "Vertical Align: ",
30177                 xns : rooui.Toolbar  //Boostrap?
30178             },
30179             {
30180                 xtype : 'ComboBox',
30181                 allowBlank : false,
30182                 displayField : 'val',
30183                 editable : true,
30184                 listWidth : 100,
30185                 triggerAction : 'all',
30186                 typeAhead : true,
30187                 valueField : 'val',
30188                 width : 100,
30189                 name : 'valign',
30190                 listeners : {
30191                     select : function (combo, r, index)
30192                     {
30193                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30194                         var b = cell();
30195                         b.valign = r.get('val');
30196                         b.updateElement();
30197                         syncValue();
30198                         toolbar.editorcore.onEditorEvent();
30199                     }
30200                 },
30201                 xns : rooui.form,
30202                 store : {
30203                     xtype : 'SimpleStore',
30204                     data : [
30205                         ['top'],
30206                         ['middle'],
30207                         ['bottom'] // there are afew more... 
30208                     ],
30209                     fields : [ 'val'],
30210                     xns : Roo.data
30211                 }
30212             },
30213             
30214             {
30215                 xtype : 'TextItem',
30216                 text : "Merge Cells: ",
30217                  xns : rooui.Toolbar 
30218                
30219             },
30220             
30221             
30222             {
30223                 xtype : 'Button',
30224                 text: 'Right',
30225                 listeners : {
30226                     click : function (_self, e)
30227                     {
30228                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30229                         cell().mergeRight();
30230                         //block().growColumn();
30231                         syncValue();
30232                         toolbar.editorcore.onEditorEvent();
30233                     }
30234                 },
30235                 xns : rooui.Toolbar
30236             },
30237              
30238             {
30239                 xtype : 'Button',
30240                 text: 'Below',
30241                 listeners : {
30242                     click : function (_self, e)
30243                     {
30244                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30245                         cell().mergeBelow();
30246                         //block().growColumn();
30247                         syncValue();
30248                         toolbar.editorcore.onEditorEvent();
30249                     }
30250                 },
30251                 xns : rooui.Toolbar
30252             },
30253             {
30254                 xtype : 'TextItem',
30255                 text : "| ",
30256                  xns : rooui.Toolbar 
30257                
30258             },
30259             
30260             {
30261                 xtype : 'Button',
30262                 text: 'Split',
30263                 listeners : {
30264                     click : function (_self, e)
30265                     {
30266                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30267                         cell().split();
30268                         syncValue();
30269                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30270                         toolbar.editorcore.onEditorEvent();
30271                                              
30272                     }
30273                 },
30274                 xns : rooui.Toolbar
30275             },
30276             {
30277                 xtype : 'Fill',
30278                 xns : rooui.Toolbar 
30279                
30280             },
30281         
30282           
30283             {
30284                 xtype : 'Button',
30285                 text: 'Delete',
30286                  
30287                 xns : rooui.Toolbar,
30288                 menu : {
30289                     xtype : 'Menu',
30290                     xns : rooui.menu,
30291                     items : [
30292                         {
30293                             xtype : 'Item',
30294                             html: 'Column',
30295                             listeners : {
30296                                 click : function (_self, e)
30297                                 {
30298                                     var t = table();
30299                                     
30300                                     cell().deleteColumn();
30301                                     syncValue();
30302                                     toolbar.editorcore.selectNode(t.node);
30303                                     toolbar.editorcore.onEditorEvent();   
30304                                 }
30305                             },
30306                             xns : rooui.menu
30307                         },
30308                         {
30309                             xtype : 'Item',
30310                             html: 'Row',
30311                             listeners : {
30312                                 click : function (_self, e)
30313                                 {
30314                                     var t = table();
30315                                     cell().deleteRow();
30316                                     syncValue();
30317                                     
30318                                     toolbar.editorcore.selectNode(t.node);
30319                                     toolbar.editorcore.onEditorEvent();   
30320                                                          
30321                                 }
30322                             },
30323                             xns : rooui.menu
30324                         },
30325                        {
30326                             xtype : 'Separator',
30327                             xns : rooui.menu
30328                         },
30329                         {
30330                             xtype : 'Item',
30331                             html: 'Table',
30332                             listeners : {
30333                                 click : function (_self, e)
30334                                 {
30335                                     var t = table();
30336                                     var nn = t.node.nextSibling || t.node.previousSibling;
30337                                     t.node.parentNode.removeChild(t.node);
30338                                     if (nn) { 
30339                                         toolbar.editorcore.selectNode(nn, true);
30340                                     }
30341                                     toolbar.editorcore.onEditorEvent();   
30342                                                          
30343                                 }
30344                             },
30345                             xns : rooui.menu
30346                         }
30347                     ]
30348                 }
30349             }
30350             
30351             // align... << fixme
30352             
30353         ];
30354         
30355     },
30356     
30357     
30358   /**
30359      * create a DomHelper friendly object - for use with
30360      * Roo.DomHelper.markup / overwrite / etc..
30361      * ?? should it be called with option to hide all editing features?
30362      */
30363  /**
30364      * create a DomHelper friendly object - for use with
30365      * Roo.DomHelper.markup / overwrite / etc..
30366      * ?? should it be called with option to hide all editing features?
30367      */
30368     toObject : function()
30369     {
30370         var ret = {
30371             tag : 'td',
30372             contenteditable : 'true', // this stops cell selection from picking the table.
30373             'data-block' : 'Td',
30374             valign : this.valign,
30375             style : {  
30376                 'text-align' :  this.textAlign,
30377                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30378                 'border-collapse' : 'collapse',
30379                 padding : '6px', // 8 for desktop / 4 for mobile
30380                 'vertical-align': this.valign
30381             },
30382             html : this.html
30383         };
30384         if (this.width != '') {
30385             ret.width = this.width;
30386             ret.style.width = this.width;
30387         }
30388         
30389         
30390         if (this.colspan > 1) {
30391             ret.colspan = this.colspan ;
30392         } 
30393         if (this.rowspan > 1) {
30394             ret.rowspan = this.rowspan ;
30395         }
30396         
30397            
30398         
30399         return ret;
30400          
30401     },
30402     
30403     readElement : function(node)
30404     {
30405         node  = node ? node : this.node ;
30406         this.width = node.style.width;
30407         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30408         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30409         this.html = node.innerHTML;
30410         if (node.style.textAlign != '') {
30411             this.textAlign = node.style.textAlign;
30412         }
30413         
30414         
30415     },
30416      
30417     // the default cell object... at present...
30418     emptyCell : function() {
30419         return {
30420             colspan :  1,
30421             rowspan :  1,
30422             textAlign : 'left',
30423             html : "&nbsp;" // is this going to be editable now?
30424         };
30425      
30426     },
30427     
30428     removeNode : function()
30429     {
30430         return this.node.closest('table');
30431          
30432     },
30433     
30434     cellData : false,
30435     
30436     colWidths : false,
30437     
30438     toTableArray  : function()
30439     {
30440         var ret = [];
30441         var tab = this.node.closest('tr').closest('table');
30442         Array.from(tab.rows).forEach(function(r, ri){
30443             ret[ri] = [];
30444         });
30445         var rn = 0;
30446         this.colWidths = [];
30447         var all_auto = true;
30448         Array.from(tab.rows).forEach(function(r, ri){
30449             
30450             var cn = 0;
30451             Array.from(r.cells).forEach(function(ce, ci){
30452                 var c =  {
30453                     cell : ce,
30454                     row : rn,
30455                     col: cn,
30456                     colspan : ce.colSpan,
30457                     rowspan : ce.rowSpan
30458                 };
30459                 if (ce.isEqualNode(this.node)) {
30460                     this.cellData = c;
30461                 }
30462                 // if we have been filled up by a row?
30463                 if (typeof(ret[rn][cn]) != 'undefined') {
30464                     while(typeof(ret[rn][cn]) != 'undefined') {
30465                         cn++;
30466                     }
30467                     c.col = cn;
30468                 }
30469                 
30470                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30471                     this.colWidths[cn] =   ce.style.width;
30472                     if (this.colWidths[cn] != '') {
30473                         all_auto = false;
30474                     }
30475                 }
30476                 
30477                 
30478                 if (c.colspan < 2 && c.rowspan < 2 ) {
30479                     ret[rn][cn] = c;
30480                     cn++;
30481                     return;
30482                 }
30483                 for(var j = 0; j < c.rowspan; j++) {
30484                     if (typeof(ret[rn+j]) == 'undefined') {
30485                         continue; // we have a problem..
30486                     }
30487                     ret[rn+j][cn] = c;
30488                     for(var i = 0; i < c.colspan; i++) {
30489                         ret[rn+j][cn+i] = c;
30490                     }
30491                 }
30492                 
30493                 cn += c.colspan;
30494             }, this);
30495             rn++;
30496         }, this);
30497         
30498         // initalize widths.?
30499         // either all widths or no widths..
30500         if (all_auto) {
30501             this.colWidths[0] = false; // no widths flag.
30502         }
30503         
30504         
30505         return ret;
30506         
30507     },
30508     
30509     
30510     
30511     
30512     mergeRight: function()
30513     {
30514          
30515         // get the contents of the next cell along..
30516         var tr = this.node.closest('tr');
30517         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30518         if (i >= tr.childNodes.length - 1) {
30519             return; // no cells on right to merge with.
30520         }
30521         var table = this.toTableArray();
30522         
30523         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30524             return; // nothing right?
30525         }
30526         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30527         // right cell - must be same rowspan and on the same row.
30528         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30529             return; // right hand side is not same rowspan.
30530         }
30531         
30532         
30533         
30534         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30535         tr.removeChild(rc.cell);
30536         this.colspan += rc.colspan;
30537         this.node.setAttribute('colspan', this.colspan);
30538
30539         var table = this.toTableArray();
30540         this.normalizeWidths(table);
30541         this.updateWidths(table);
30542     },
30543     
30544     
30545     mergeBelow : function()
30546     {
30547         var table = this.toTableArray();
30548         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30549             return; // no row below
30550         }
30551         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30552             return; // nothing right?
30553         }
30554         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30555         
30556         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30557             return; // right hand side is not same rowspan.
30558         }
30559         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30560         rc.cell.parentNode.removeChild(rc.cell);
30561         this.rowspan += rc.rowspan;
30562         this.node.setAttribute('rowspan', this.rowspan);
30563     },
30564     
30565     split: function()
30566     {
30567         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30568             return;
30569         }
30570         var table = this.toTableArray();
30571         var cd = this.cellData;
30572         this.rowspan = 1;
30573         this.colspan = 1;
30574         
30575         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30576              
30577             
30578             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30579                 if (r == cd.row && c == cd.col) {
30580                     this.node.removeAttribute('rowspan');
30581                     this.node.removeAttribute('colspan');
30582                 }
30583                  
30584                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30585                 ntd.removeAttribute('id'); 
30586                 ntd.style.width  = this.colWidths[c];
30587                 ntd.innerHTML = '';
30588                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30589             }
30590             
30591         }
30592         this.redrawAllCells(table);
30593         
30594     },
30595     
30596     
30597     
30598     redrawAllCells: function(table)
30599     {
30600         
30601          
30602         var tab = this.node.closest('tr').closest('table');
30603         var ctr = tab.rows[0].parentNode;
30604         Array.from(tab.rows).forEach(function(r, ri){
30605             
30606             Array.from(r.cells).forEach(function(ce, ci){
30607                 ce.parentNode.removeChild(ce);
30608             });
30609             r.parentNode.removeChild(r);
30610         });
30611         for(var r = 0 ; r < table.length; r++) {
30612             var re = tab.rows[r];
30613             
30614             var re = tab.ownerDocument.createElement('tr');
30615             ctr.appendChild(re);
30616             for(var c = 0 ; c < table[r].length; c++) {
30617                 if (table[r][c].cell === false) {
30618                     continue;
30619                 }
30620                 
30621                 re.appendChild(table[r][c].cell);
30622                  
30623                 table[r][c].cell = false;
30624             }
30625         }
30626         
30627     },
30628     updateWidths : function(table)
30629     {
30630         for(var r = 0 ; r < table.length; r++) {
30631            
30632             for(var c = 0 ; c < table[r].length; c++) {
30633                 if (table[r][c].cell === false) {
30634                     continue;
30635                 }
30636                 
30637                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30638                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30639                     el.width = Math.floor(this.colWidths[c])  +'%';
30640                     el.updateElement(el.node);
30641                 }
30642                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30643                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30644                     var width = 0;
30645                     for(var i = 0; i < table[r][c].colspan; i ++) {
30646                         width += Math.floor(this.colWidths[c + i]);
30647                     }
30648                     el.width = width  +'%';
30649                     el.updateElement(el.node);
30650                 }
30651                 table[r][c].cell = false; // done
30652             }
30653         }
30654     },
30655     normalizeWidths : function(table)
30656     {
30657         if (this.colWidths[0] === false) {
30658             var nw = 100.0 / this.colWidths.length;
30659             this.colWidths.forEach(function(w,i) {
30660                 this.colWidths[i] = nw;
30661             },this);
30662             return;
30663         }
30664     
30665         var t = 0, missing = [];
30666         
30667         this.colWidths.forEach(function(w,i) {
30668             //if you mix % and
30669             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30670             var add =  this.colWidths[i];
30671             if (add > 0) {
30672                 t+=add;
30673                 return;
30674             }
30675             missing.push(i);
30676             
30677             
30678         },this);
30679         var nc = this.colWidths.length;
30680         if (missing.length) {
30681             var mult = (nc - missing.length) / (1.0 * nc);
30682             var t = mult * t;
30683             var ew = (100 -t) / (1.0 * missing.length);
30684             this.colWidths.forEach(function(w,i) {
30685                 if (w > 0) {
30686                     this.colWidths[i] = w * mult;
30687                     return;
30688                 }
30689                 
30690                 this.colWidths[i] = ew;
30691             }, this);
30692             // have to make up numbers..
30693              
30694         }
30695         // now we should have all the widths..
30696         
30697     
30698     },
30699     
30700     shrinkColumn : function()
30701     {
30702         var table = this.toTableArray();
30703         this.normalizeWidths(table);
30704         var col = this.cellData.col;
30705         var nw = this.colWidths[col] * 0.8;
30706         if (nw < 5) {
30707             return;
30708         }
30709         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30710         this.colWidths.forEach(function(w,i) {
30711             if (i == col) {
30712                  this.colWidths[i] = nw;
30713                 return;
30714             }
30715             this.colWidths[i] += otherAdd
30716         }, this);
30717         this.updateWidths(table);
30718          
30719     },
30720     growColumn : function()
30721     {
30722         var table = this.toTableArray();
30723         this.normalizeWidths(table);
30724         var col = this.cellData.col;
30725         var nw = this.colWidths[col] * 1.2;
30726         if (nw > 90) {
30727             return;
30728         }
30729         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30730         this.colWidths.forEach(function(w,i) {
30731             if (i == col) {
30732                 this.colWidths[i] = nw;
30733                 return;
30734             }
30735             this.colWidths[i] -= otherSub
30736         }, this);
30737         this.updateWidths(table);
30738          
30739     },
30740     deleteRow : function()
30741     {
30742         // delete this rows 'tr'
30743         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30744         // then reduce the rowspan.
30745         var table = this.toTableArray();
30746         // this.cellData.row;
30747         for (var i =0;i< table[this.cellData.row].length ; i++) {
30748             var c = table[this.cellData.row][i];
30749             if (c.row != this.cellData.row) {
30750                 
30751                 c.rowspan--;
30752                 c.cell.setAttribute('rowspan', c.rowspan);
30753                 continue;
30754             }
30755             if (c.rowspan > 1) {
30756                 c.rowspan--;
30757                 c.cell.setAttribute('rowspan', c.rowspan);
30758             }
30759         }
30760         table.splice(this.cellData.row,1);
30761         this.redrawAllCells(table);
30762         
30763     },
30764     deleteColumn : function()
30765     {
30766         var table = this.toTableArray();
30767         
30768         for (var i =0;i< table.length ; i++) {
30769             var c = table[i][this.cellData.col];
30770             if (c.col != this.cellData.col) {
30771                 table[i][this.cellData.col].colspan--;
30772             } else if (c.colspan > 1) {
30773                 c.colspan--;
30774                 c.cell.setAttribute('colspan', c.colspan);
30775             }
30776             table[i].splice(this.cellData.col,1);
30777         }
30778         
30779         this.redrawAllCells(table);
30780     }
30781     
30782     
30783     
30784     
30785 })
30786
30787 //<script type="text/javascript">
30788
30789 /*
30790  * Based  Ext JS Library 1.1.1
30791  * Copyright(c) 2006-2007, Ext JS, LLC.
30792  * LGPL
30793  *
30794  */
30795  
30796 /**
30797  * @class Roo.HtmlEditorCore
30798  * @extends Roo.Component
30799  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30800  *
30801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30802  */
30803
30804 Roo.HtmlEditorCore = function(config){
30805     
30806     
30807     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30808     
30809     
30810     this.addEvents({
30811         /**
30812          * @event initialize
30813          * Fires when the editor is fully initialized (including the iframe)
30814          * @param {Roo.HtmlEditorCore} this
30815          */
30816         initialize: true,
30817         /**
30818          * @event activate
30819          * Fires when the editor is first receives the focus. Any insertion must wait
30820          * until after this event.
30821          * @param {Roo.HtmlEditorCore} this
30822          */
30823         activate: true,
30824          /**
30825          * @event beforesync
30826          * Fires before the textarea is updated with content from the editor iframe. Return false
30827          * to cancel the sync.
30828          * @param {Roo.HtmlEditorCore} this
30829          * @param {String} html
30830          */
30831         beforesync: true,
30832          /**
30833          * @event beforepush
30834          * Fires before the iframe editor is updated with content from the textarea. Return false
30835          * to cancel the push.
30836          * @param {Roo.HtmlEditorCore} this
30837          * @param {String} html
30838          */
30839         beforepush: true,
30840          /**
30841          * @event sync
30842          * Fires when the textarea is updated with content from the editor iframe.
30843          * @param {Roo.HtmlEditorCore} this
30844          * @param {String} html
30845          */
30846         sync: true,
30847          /**
30848          * @event push
30849          * Fires when the iframe editor is updated with content from the textarea.
30850          * @param {Roo.HtmlEditorCore} this
30851          * @param {String} html
30852          */
30853         push: true,
30854         
30855         /**
30856          * @event editorevent
30857          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30858          * @param {Roo.HtmlEditorCore} this
30859          */
30860         editorevent: true 
30861         
30862         
30863     });
30864     
30865     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30866     
30867     // defaults : white / black...
30868     this.applyBlacklists();
30869     
30870     
30871     
30872 };
30873
30874
30875 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30876
30877
30878      /**
30879      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30880      */
30881     
30882     owner : false,
30883     
30884      /**
30885      * @cfg {String} css styling for resizing. (used on bootstrap only)
30886      */
30887     resize : false,
30888      /**
30889      * @cfg {Number} height (in pixels)
30890      */   
30891     height: 300,
30892    /**
30893      * @cfg {Number} width (in pixels)
30894      */   
30895     width: 500,
30896      /**
30897      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30898      *         if you are doing an email editor, this probably needs disabling, it's designed
30899      */
30900     autoClean: true,
30901     
30902     /**
30903      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30904      */
30905     enableBlocks : true,
30906     /**
30907      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30908      * 
30909      */
30910     stylesheets: false,
30911      /**
30912      * @cfg {String} language default en - language of text (usefull for rtl languages)
30913      * 
30914      */
30915     language: 'en',
30916     
30917     /**
30918      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30919      *          - by default they are stripped - if you are editing email you may need this.
30920      */
30921     allowComments: false,
30922     // id of frame..
30923     frameId: false,
30924     
30925     // private properties
30926     validationEvent : false,
30927     deferHeight: true,
30928     initialized : false,
30929     activated : false,
30930     sourceEditMode : false,
30931     onFocus : Roo.emptyFn,
30932     iframePad:3,
30933     hideMode:'offsets',
30934     
30935     clearUp: true,
30936     
30937     // blacklist + whitelisted elements..
30938     black: false,
30939     white: false,
30940      
30941     bodyCls : '',
30942
30943     
30944     undoManager : false,
30945     /**
30946      * Protected method that will not generally be called directly. It
30947      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30948      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30949      */
30950     getDocMarkup : function(){
30951         // body styles..
30952         var st = '';
30953         
30954         // inherit styels from page...?? 
30955         if (this.stylesheets === false) {
30956             
30957             Roo.get(document.head).select('style').each(function(node) {
30958                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30959             });
30960             
30961             Roo.get(document.head).select('link').each(function(node) { 
30962                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30963             });
30964             
30965         } else if (!this.stylesheets.length) {
30966                 // simple..
30967                 st = '<style type="text/css">' +
30968                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30969                    '</style>';
30970         } else {
30971             for (var i in this.stylesheets) {
30972                 if (typeof(this.stylesheets[i]) != 'string') {
30973                     continue;
30974                 }
30975                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30976             }
30977             
30978         }
30979         
30980         st +=  '<style type="text/css">' +
30981             'IMG { cursor: pointer } ' +
30982         '</style>';
30983         
30984         st += '<meta name="google" content="notranslate">';
30985         
30986         var cls = 'notranslate roo-htmleditor-body';
30987         
30988         if(this.bodyCls.length){
30989             cls += ' ' + this.bodyCls;
30990         }
30991         
30992         return '<html  class="notranslate" translate="no"><head>' + st  +
30993             //<style type="text/css">' +
30994             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30995             //'</style>' +
30996             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30997     },
30998
30999     // private
31000     onRender : function(ct, position)
31001     {
31002         var _t = this;
31003         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31004         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31005         
31006         
31007         this.el.dom.style.border = '0 none';
31008         this.el.dom.setAttribute('tabIndex', -1);
31009         this.el.addClass('x-hidden hide');
31010         
31011         
31012         
31013         if(Roo.isIE){ // fix IE 1px bogus margin
31014             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31015         }
31016        
31017         
31018         this.frameId = Roo.id();
31019         
31020         var ifcfg = {
31021             tag: 'iframe',
31022             cls: 'form-control', // bootstrap..
31023             id: this.frameId,
31024             name: this.frameId,
31025             frameBorder : 'no',
31026             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31027         };
31028         if (this.resize) {
31029             ifcfg.style = { resize : this.resize };
31030         }
31031         
31032         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31033         
31034         
31035         this.iframe = iframe.dom;
31036
31037         this.assignDocWin();
31038         
31039         this.doc.designMode = 'on';
31040        
31041         this.doc.open();
31042         this.doc.write(this.getDocMarkup());
31043         this.doc.close();
31044
31045         
31046         var task = { // must defer to wait for browser to be ready
31047             run : function(){
31048                 //console.log("run task?" + this.doc.readyState);
31049                 this.assignDocWin();
31050                 if(this.doc.body || this.doc.readyState == 'complete'){
31051                     try {
31052                         this.doc.designMode="on";
31053                         
31054                     } catch (e) {
31055                         return;
31056                     }
31057                     Roo.TaskMgr.stop(task);
31058                     this.initEditor.defer(10, this);
31059                 }
31060             },
31061             interval : 10,
31062             duration: 10000,
31063             scope: this
31064         };
31065         Roo.TaskMgr.start(task);
31066
31067     },
31068
31069     // private
31070     onResize : function(w, h)
31071     {
31072          Roo.log('resize: ' +w + ',' + h );
31073         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31074         if(!this.iframe){
31075             return;
31076         }
31077         if(typeof w == 'number'){
31078             
31079             this.iframe.style.width = w + 'px';
31080         }
31081         if(typeof h == 'number'){
31082             
31083             this.iframe.style.height = h + 'px';
31084             if(this.doc){
31085                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31086             }
31087         }
31088         
31089     },
31090
31091     /**
31092      * Toggles the editor between standard and source edit mode.
31093      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31094      */
31095     toggleSourceEdit : function(sourceEditMode){
31096         
31097         this.sourceEditMode = sourceEditMode === true;
31098         
31099         if(this.sourceEditMode){
31100  
31101             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31102             
31103         }else{
31104             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31105             //this.iframe.className = '';
31106             this.deferFocus();
31107         }
31108         //this.setSize(this.owner.wrap.getSize());
31109         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31110     },
31111
31112     
31113   
31114
31115     /**
31116      * Protected method that will not generally be called directly. If you need/want
31117      * custom HTML cleanup, this is the method you should override.
31118      * @param {String} html The HTML to be cleaned
31119      * return {String} The cleaned HTML
31120      */
31121     cleanHtml : function(html)
31122     {
31123         html = String(html);
31124         if(html.length > 5){
31125             if(Roo.isSafari){ // strip safari nonsense
31126                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31127             }
31128         }
31129         if(html == '&nbsp;'){
31130             html = '';
31131         }
31132         return html;
31133     },
31134
31135     /**
31136      * HTML Editor -> Textarea
31137      * Protected method that will not generally be called directly. Syncs the contents
31138      * of the editor iframe with the textarea.
31139      */
31140     syncValue : function()
31141     {
31142         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31143         if(this.initialized){
31144             
31145             if (this.undoManager) {
31146                 this.undoManager.addEvent();
31147             }
31148
31149             
31150             var bd = (this.doc.body || this.doc.documentElement);
31151            
31152             
31153             var sel = this.win.getSelection();
31154             
31155             var div = document.createElement('div');
31156             div.innerHTML = bd.innerHTML;
31157             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31158             if (gtx.length > 0) {
31159                 var rm = gtx.item(0).parentNode;
31160                 rm.parentNode.removeChild(rm);
31161             }
31162             
31163            
31164             if (this.enableBlocks) {
31165                 new Roo.htmleditor.FilterBlock({ node : div });
31166             }
31167             
31168             var html = div.innerHTML;
31169             
31170             //?? tidy?
31171             if (this.autoClean) {
31172                 
31173                 new Roo.htmleditor.FilterAttributes({
31174                     node : div,
31175                     attrib_white : [
31176                             'href',
31177                             'src',
31178                             'name',
31179                             'align',
31180                             'colspan',
31181                             'rowspan',
31182                             'data-display',
31183                             'data-width',
31184                             'data-caption',
31185                             'start' ,
31186                             'style',
31187                             // youtube embed.
31188                             'class',
31189                             'allowfullscreen',
31190                             'frameborder',
31191                             'width',
31192                             'height',
31193                             'alt'
31194                             ],
31195                     attrib_clean : ['href', 'src' ] 
31196                 });
31197                 
31198                 var tidy = new Roo.htmleditor.TidySerializer({
31199                     inner:  true
31200                 });
31201                 html  = tidy.serialize(div);
31202                 
31203             }
31204             
31205             
31206             if(Roo.isSafari){
31207                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31208                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31209                 if(m && m[1]){
31210                     html = '<div style="'+m[0]+'">' + html + '</div>';
31211                 }
31212             }
31213             html = this.cleanHtml(html);
31214             // fix up the special chars.. normaly like back quotes in word...
31215             // however we do not want to do this with chinese..
31216             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31217                 
31218                 var cc = match.charCodeAt();
31219
31220                 // Get the character value, handling surrogate pairs
31221                 if (match.length == 2) {
31222                     // It's a surrogate pair, calculate the Unicode code point
31223                     var high = match.charCodeAt(0) - 0xD800;
31224                     var low  = match.charCodeAt(1) - 0xDC00;
31225                     cc = (high * 0x400) + low + 0x10000;
31226                 }  else if (
31227                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31228                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31229                     (cc >= 0xf900 && cc < 0xfb00 )
31230                 ) {
31231                         return match;
31232                 }  
31233          
31234                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31235                 return "&#" + cc + ";";
31236                 
31237                 
31238             });
31239             
31240             
31241              
31242             if(this.owner.fireEvent('beforesync', this, html) !== false){
31243                 this.el.dom.value = html;
31244                 this.owner.fireEvent('sync', this, html);
31245             }
31246         }
31247     },
31248
31249     /**
31250      * TEXTAREA -> EDITABLE
31251      * Protected method that will not generally be called directly. Pushes the value of the textarea
31252      * into the iframe editor.
31253      */
31254     pushValue : function()
31255     {
31256         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31257         if(this.initialized){
31258             var v = this.el.dom.value.trim();
31259             
31260             
31261             if(this.owner.fireEvent('beforepush', this, v) !== false){
31262                 var d = (this.doc.body || this.doc.documentElement);
31263                 d.innerHTML = v;
31264                  
31265                 this.el.dom.value = d.innerHTML;
31266                 this.owner.fireEvent('push', this, v);
31267             }
31268             if (this.autoClean) {
31269                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31270                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31271             }
31272             if (this.enableBlocks) {
31273                 Roo.htmleditor.Block.initAll(this.doc.body);
31274             }
31275             
31276             this.updateLanguage();
31277             
31278             var lc = this.doc.body.lastChild;
31279             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31280                 // add an extra line at the end.
31281                 this.doc.body.appendChild(this.doc.createElement('br'));
31282             }
31283             
31284             
31285         }
31286     },
31287
31288     // private
31289     deferFocus : function(){
31290         this.focus.defer(10, this);
31291     },
31292
31293     // doc'ed in Field
31294     focus : function(){
31295         if(this.win && !this.sourceEditMode){
31296             this.win.focus();
31297         }else{
31298             this.el.focus();
31299         }
31300     },
31301     
31302     assignDocWin: function()
31303     {
31304         var iframe = this.iframe;
31305         
31306          if(Roo.isIE){
31307             this.doc = iframe.contentWindow.document;
31308             this.win = iframe.contentWindow;
31309         } else {
31310 //            if (!Roo.get(this.frameId)) {
31311 //                return;
31312 //            }
31313 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31314 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31315             
31316             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31317                 return;
31318             }
31319             
31320             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31321             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31322         }
31323     },
31324     
31325     // private
31326     initEditor : function(){
31327         //console.log("INIT EDITOR");
31328         this.assignDocWin();
31329         
31330         
31331         
31332         this.doc.designMode="on";
31333         this.doc.open();
31334         this.doc.write(this.getDocMarkup());
31335         this.doc.close();
31336         
31337         var dbody = (this.doc.body || this.doc.documentElement);
31338         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31339         // this copies styles from the containing element into thsi one..
31340         // not sure why we need all of this..
31341         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31342         
31343         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31344         //ss['background-attachment'] = 'fixed'; // w3c
31345         dbody.bgProperties = 'fixed'; // ie
31346         dbody.setAttribute("translate", "no");
31347         
31348         //Roo.DomHelper.applyStyles(dbody, ss);
31349         Roo.EventManager.on(this.doc, {
31350              
31351             'mouseup': this.onEditorEvent,
31352             'dblclick': this.onEditorEvent,
31353             'click': this.onEditorEvent,
31354             'keyup': this.onEditorEvent,
31355             
31356             buffer:100,
31357             scope: this
31358         });
31359         Roo.EventManager.on(this.doc, {
31360             'paste': this.onPasteEvent,
31361             scope : this
31362         });
31363         if(Roo.isGecko){
31364             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31365         }
31366         //??? needed???
31367         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31368             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31369         }
31370         this.initialized = true;
31371
31372         
31373         // initialize special key events - enter
31374         new Roo.htmleditor.KeyEnter({core : this});
31375         
31376          
31377         
31378         this.owner.fireEvent('initialize', this);
31379         this.pushValue();
31380     },
31381     // this is to prevent a href clicks resulting in a redirect?
31382    
31383     onPasteEvent : function(e,v)
31384     {
31385         // I think we better assume paste is going to be a dirty load of rubish from word..
31386         
31387         // even pasting into a 'email version' of this widget will have to clean up that mess.
31388         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31389         
31390         // check what type of paste - if it's an image, then handle it differently.
31391         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31392             // pasting images? 
31393             var urlAPI = (window.createObjectURL && window) || 
31394                 (window.URL && URL.revokeObjectURL && URL) || 
31395                 (window.webkitURL && webkitURL);
31396             
31397             var r = new FileReader();
31398             var t = this;
31399             r.addEventListener('load',function()
31400             {
31401                 
31402                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31403                 // is insert asycn?
31404                 if (t.enableBlocks) {
31405                     
31406                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31407                         if (img.closest('figure')) { // assume!! that it's aready
31408                             return;
31409                         }
31410                         var fig  = new Roo.htmleditor.BlockFigure({
31411                             image_src  : img.src
31412                         });
31413                         fig.updateElement(img); // replace it..
31414                         
31415                     });
31416                 }
31417                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31418                 t.owner.fireEvent('paste', this);
31419             });
31420             r.readAsDataURL(cd.files[0]);
31421             
31422             e.preventDefault();
31423             
31424             return false;
31425         }
31426         if (cd.types.indexOf('text/html') < 0 ) {
31427             return false;
31428         }
31429         var images = [];
31430         var html = cd.getData('text/html'); // clipboard event
31431         if (cd.types.indexOf('text/rtf') > -1) {
31432             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31433             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31434         }
31435         //Roo.log(images);
31436         //Roo.log(imgs);
31437         // fixme..
31438         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31439                        .map(function(g) { return g.toDataURL(); })
31440                        .filter(function(g) { return g != 'about:blank'; });
31441         
31442         //Roo.log(html);
31443         html = this.cleanWordChars(html);
31444         
31445         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31446         
31447         
31448         var sn = this.getParentElement();
31449         // check if d contains a table, and prevent nesting??
31450         //Roo.log(d.getElementsByTagName('table'));
31451         //Roo.log(sn);
31452         //Roo.log(sn.closest('table'));
31453         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31454             e.preventDefault();
31455             this.insertAtCursor("You can not nest tables");
31456             //Roo.log("prevent?"); // fixme - 
31457             return false;
31458         }
31459         
31460         
31461         
31462         if (images.length > 0) {
31463             // replace all v:imagedata - with img.
31464             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31465             Roo.each(ar, function(node) {
31466                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31467                 node.parentNode.removeChild(node);
31468             });
31469             
31470             
31471             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31472                 img.setAttribute('src', images[i]);
31473             });
31474         }
31475         if (this.autoClean) {
31476             new Roo.htmleditor.FilterWord({ node : d });
31477             
31478             new Roo.htmleditor.FilterStyleToTag({ node : d });
31479             new Roo.htmleditor.FilterAttributes({
31480                 node : d,
31481                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31482                 attrib_clean : ['href', 'src' ] 
31483             });
31484             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31485             // should be fonts..
31486             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31487             new Roo.htmleditor.FilterParagraph({ node : d });
31488             new Roo.htmleditor.FilterSpan({ node : d });
31489             new Roo.htmleditor.FilterLongBr({ node : d });
31490             new Roo.htmleditor.FilterComment({ node : d });
31491             
31492             
31493         }
31494         if (this.enableBlocks) {
31495                 
31496             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31497                 if (img.closest('figure')) { // assume!! that it's aready
31498                     return;
31499                 }
31500                 var fig  = new Roo.htmleditor.BlockFigure({
31501                     image_src  : img.src
31502                 });
31503                 fig.updateElement(img); // replace it..
31504                 
31505             });
31506         }
31507         
31508         
31509         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31510         if (this.enableBlocks) {
31511             Roo.htmleditor.Block.initAll(this.doc.body);
31512         }
31513          
31514         
31515         e.preventDefault();
31516         this.owner.fireEvent('paste', this);
31517         return false;
31518         // default behaveiour should be our local cleanup paste? (optional?)
31519         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31520         //this.owner.fireEvent('paste', e, v);
31521     },
31522     // private
31523     onDestroy : function(){
31524         
31525         
31526         
31527         if(this.rendered){
31528             
31529             //for (var i =0; i < this.toolbars.length;i++) {
31530             //    // fixme - ask toolbars for heights?
31531             //    this.toolbars[i].onDestroy();
31532            // }
31533             
31534             //this.wrap.dom.innerHTML = '';
31535             //this.wrap.remove();
31536         }
31537     },
31538
31539     // private
31540     onFirstFocus : function(){
31541         
31542         this.assignDocWin();
31543         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31544         
31545         this.activated = true;
31546          
31547     
31548         if(Roo.isGecko){ // prevent silly gecko errors
31549             this.win.focus();
31550             var s = this.win.getSelection();
31551             if(!s.focusNode || s.focusNode.nodeType != 3){
31552                 var r = s.getRangeAt(0);
31553                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31554                 r.collapse(true);
31555                 this.deferFocus();
31556             }
31557             try{
31558                 this.execCmd('useCSS', true);
31559                 this.execCmd('styleWithCSS', false);
31560             }catch(e){}
31561         }
31562         this.owner.fireEvent('activate', this);
31563     },
31564
31565     // private
31566     adjustFont: function(btn){
31567         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31568         //if(Roo.isSafari){ // safari
31569         //    adjust *= 2;
31570        // }
31571         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31572         if(Roo.isSafari){ // safari
31573             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31574             v =  (v < 10) ? 10 : v;
31575             v =  (v > 48) ? 48 : v;
31576             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31577             
31578         }
31579         
31580         
31581         v = Math.max(1, v+adjust);
31582         
31583         this.execCmd('FontSize', v  );
31584     },
31585
31586     onEditorEvent : function(e)
31587     {
31588          
31589         
31590         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31591             return; // we do not handle this.. (undo manager does..)
31592         }
31593         // clicking a 'block'?
31594         
31595         // in theory this detects if the last element is not a br, then we try and do that.
31596         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31597         if (e &&
31598             e.target.nodeName == 'BODY' &&
31599             e.type == "mouseup" &&
31600             this.doc.body.lastChild
31601            ) {
31602             var lc = this.doc.body.lastChild;
31603             // gtx-trans is google translate plugin adding crap.
31604             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31605                 lc = lc.previousSibling;
31606             }
31607             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31608             // if last element is <BR> - then dont do anything.
31609             
31610                 var ns = this.doc.createElement('br');
31611                 this.doc.body.appendChild(ns);
31612                 range = this.doc.createRange();
31613                 range.setStartAfter(ns);
31614                 range.collapse(true);
31615                 var sel = this.win.getSelection();
31616                 sel.removeAllRanges();
31617                 sel.addRange(range);
31618             }
31619         }
31620         
31621         
31622         
31623         this.fireEditorEvent(e);
31624       //  this.updateToolbar();
31625         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31626     },
31627     
31628     fireEditorEvent: function(e)
31629     {
31630         this.owner.fireEvent('editorevent', this, e);
31631     },
31632
31633     insertTag : function(tg)
31634     {
31635         // could be a bit smarter... -> wrap the current selected tRoo..
31636         if (tg.toLowerCase() == 'span' ||
31637             tg.toLowerCase() == 'code' ||
31638             tg.toLowerCase() == 'sup' ||
31639             tg.toLowerCase() == 'sub' 
31640             ) {
31641             
31642             range = this.createRange(this.getSelection());
31643             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31644             wrappingNode.appendChild(range.extractContents());
31645             range.insertNode(wrappingNode);
31646
31647             return;
31648             
31649             
31650             
31651         }
31652         this.execCmd("formatblock",   tg);
31653         this.undoManager.addEvent(); 
31654     },
31655     
31656     insertText : function(txt)
31657     {
31658         
31659         
31660         var range = this.createRange();
31661         range.deleteContents();
31662                //alert(Sender.getAttribute('label'));
31663                
31664         range.insertNode(this.doc.createTextNode(txt));
31665         this.undoManager.addEvent();
31666     } ,
31667     
31668      
31669
31670     /**
31671      * Executes a Midas editor command on the editor document and performs necessary focus and
31672      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31673      * @param {String} cmd The Midas command
31674      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31675      */
31676     relayCmd : function(cmd, value)
31677     {
31678         
31679         switch (cmd) {
31680             case 'justifyleft':
31681             case 'justifyright':
31682             case 'justifycenter':
31683                 // if we are in a cell, then we will adjust the
31684                 var n = this.getParentElement();
31685                 var td = n.closest('td');
31686                 if (td) {
31687                     var bl = Roo.htmleditor.Block.factory(td);
31688                     bl.textAlign = cmd.replace('justify','');
31689                     bl.updateElement();
31690                     this.owner.fireEvent('editorevent', this);
31691                     return;
31692                 }
31693                 this.execCmd('styleWithCSS', true); // 
31694                 break;
31695             case 'bold':
31696             case 'italic':
31697             case 'underline':                
31698                 // if there is no selection, then we insert, and set the curson inside it..
31699                 this.execCmd('styleWithCSS', false); 
31700                 break;
31701                 
31702         
31703             default:
31704                 break;
31705         }
31706         
31707         
31708         this.win.focus();
31709         this.execCmd(cmd, value);
31710         this.owner.fireEvent('editorevent', this);
31711         //this.updateToolbar();
31712         this.owner.deferFocus();
31713     },
31714
31715     /**
31716      * Executes a Midas editor command directly on the editor document.
31717      * For visual commands, you should use {@link #relayCmd} instead.
31718      * <b>This should only be called after the editor is initialized.</b>
31719      * @param {String} cmd The Midas command
31720      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31721      */
31722     execCmd : function(cmd, value){
31723         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31724         this.syncValue();
31725     },
31726  
31727  
31728    
31729     /**
31730      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31731      * to insert tRoo.
31732      * @param {String} text | dom node.. 
31733      */
31734     insertAtCursor : function(text)
31735     {
31736         
31737         if(!this.activated){
31738             return;
31739         }
31740          
31741         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31742             this.win.focus();
31743             
31744             
31745             // from jquery ui (MIT licenced)
31746             var range, node;
31747             var win = this.win;
31748             
31749             if (win.getSelection && win.getSelection().getRangeAt) {
31750                 
31751                 // delete the existing?
31752                 
31753                 this.createRange(this.getSelection()).deleteContents();
31754                 range = win.getSelection().getRangeAt(0);
31755                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31756                 range.insertNode(node);
31757                 range = range.cloneRange();
31758                 range.collapse(false);
31759                  
31760                 win.getSelection().removeAllRanges();
31761                 win.getSelection().addRange(range);
31762                 
31763                 
31764                 
31765             } else if (win.document.selection && win.document.selection.createRange) {
31766                 // no firefox support
31767                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31768                 win.document.selection.createRange().pasteHTML(txt);
31769             
31770             } else {
31771                 // no firefox support
31772                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31773                 this.execCmd('InsertHTML', txt);
31774             } 
31775             this.syncValue();
31776             
31777             this.deferFocus();
31778         }
31779     },
31780  // private
31781     mozKeyPress : function(e){
31782         if(e.ctrlKey){
31783             var c = e.getCharCode(), cmd;
31784           
31785             if(c > 0){
31786                 c = String.fromCharCode(c).toLowerCase();
31787                 switch(c){
31788                     case 'b':
31789                         cmd = 'bold';
31790                         break;
31791                     case 'i':
31792                         cmd = 'italic';
31793                         break;
31794                     
31795                     case 'u':
31796                         cmd = 'underline';
31797                         break;
31798                     
31799                     //case 'v':
31800                       //  this.cleanUpPaste.defer(100, this);
31801                       //  return;
31802                         
31803                 }
31804                 if(cmd){
31805                     
31806                     this.relayCmd(cmd);
31807                     //this.win.focus();
31808                     //this.execCmd(cmd);
31809                     //this.deferFocus();
31810                     e.preventDefault();
31811                 }
31812                 
31813             }
31814         }
31815     },
31816
31817     // private
31818     fixKeys : function(){ // load time branching for fastest keydown performance
31819         
31820         
31821         if(Roo.isIE){
31822             return function(e){
31823                 var k = e.getKey(), r;
31824                 if(k == e.TAB){
31825                     e.stopEvent();
31826                     r = this.doc.selection.createRange();
31827                     if(r){
31828                         r.collapse(true);
31829                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31830                         this.deferFocus();
31831                     }
31832                     return;
31833                 }
31834                 /// this is handled by Roo.htmleditor.KeyEnter
31835                  /*
31836                 if(k == e.ENTER){
31837                     r = this.doc.selection.createRange();
31838                     if(r){
31839                         var target = r.parentElement();
31840                         if(!target || target.tagName.toLowerCase() != 'li'){
31841                             e.stopEvent();
31842                             r.pasteHTML('<br/>');
31843                             r.collapse(false);
31844                             r.select();
31845                         }
31846                     }
31847                 }
31848                 */
31849                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31850                 //    this.cleanUpPaste.defer(100, this);
31851                 //    return;
31852                 //}
31853                 
31854                 
31855             };
31856         }else if(Roo.isOpera){
31857             return function(e){
31858                 var k = e.getKey();
31859                 if(k == e.TAB){
31860                     e.stopEvent();
31861                     this.win.focus();
31862                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31863                     this.deferFocus();
31864                 }
31865                
31866                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31867                 //    this.cleanUpPaste.defer(100, this);
31868                  //   return;
31869                 //}
31870                 
31871             };
31872         }else if(Roo.isSafari){
31873             return function(e){
31874                 var k = e.getKey();
31875                 
31876                 if(k == e.TAB){
31877                     e.stopEvent();
31878                     this.execCmd('InsertText','\t');
31879                     this.deferFocus();
31880                     return;
31881                 }
31882                  this.mozKeyPress(e);
31883                 
31884                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31885                  //   this.cleanUpPaste.defer(100, this);
31886                  //   return;
31887                // }
31888                 
31889              };
31890         }
31891     }(),
31892     
31893     getAllAncestors: function()
31894     {
31895         var p = this.getSelectedNode();
31896         var a = [];
31897         if (!p) {
31898             a.push(p); // push blank onto stack..
31899             p = this.getParentElement();
31900         }
31901         
31902         
31903         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31904             a.push(p);
31905             p = p.parentNode;
31906         }
31907         a.push(this.doc.body);
31908         return a;
31909     },
31910     lastSel : false,
31911     lastSelNode : false,
31912     
31913     
31914     getSelection : function() 
31915     {
31916         this.assignDocWin();
31917         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31918     },
31919     /**
31920      * Select a dom node
31921      * @param {DomElement} node the node to select
31922      */
31923     selectNode : function(node, collapse)
31924     {
31925         var nodeRange = node.ownerDocument.createRange();
31926         try {
31927             nodeRange.selectNode(node);
31928         } catch (e) {
31929             nodeRange.selectNodeContents(node);
31930         }
31931         if (collapse === true) {
31932             nodeRange.collapse(true);
31933         }
31934         //
31935         var s = this.win.getSelection();
31936         s.removeAllRanges();
31937         s.addRange(nodeRange);
31938     },
31939     
31940     getSelectedNode: function() 
31941     {
31942         // this may only work on Gecko!!!
31943         
31944         // should we cache this!!!!
31945         
31946          
31947          
31948         var range = this.createRange(this.getSelection()).cloneRange();
31949         
31950         if (Roo.isIE) {
31951             var parent = range.parentElement();
31952             while (true) {
31953                 var testRange = range.duplicate();
31954                 testRange.moveToElementText(parent);
31955                 if (testRange.inRange(range)) {
31956                     break;
31957                 }
31958                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31959                     break;
31960                 }
31961                 parent = parent.parentElement;
31962             }
31963             return parent;
31964         }
31965         
31966         // is ancestor a text element.
31967         var ac =  range.commonAncestorContainer;
31968         if (ac.nodeType == 3) {
31969             ac = ac.parentNode;
31970         }
31971         
31972         var ar = ac.childNodes;
31973          
31974         var nodes = [];
31975         var other_nodes = [];
31976         var has_other_nodes = false;
31977         for (var i=0;i<ar.length;i++) {
31978             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31979                 continue;
31980             }
31981             // fullly contained node.
31982             
31983             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31984                 nodes.push(ar[i]);
31985                 continue;
31986             }
31987             
31988             // probably selected..
31989             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31990                 other_nodes.push(ar[i]);
31991                 continue;
31992             }
31993             // outer..
31994             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31995                 continue;
31996             }
31997             
31998             
31999             has_other_nodes = true;
32000         }
32001         if (!nodes.length && other_nodes.length) {
32002             nodes= other_nodes;
32003         }
32004         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32005             return false;
32006         }
32007         
32008         return nodes[0];
32009     },
32010     
32011     
32012     createRange: function(sel)
32013     {
32014         // this has strange effects when using with 
32015         // top toolbar - not sure if it's a great idea.
32016         //this.editor.contentWindow.focus();
32017         if (typeof sel != "undefined") {
32018             try {
32019                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32020             } catch(e) {
32021                 return this.doc.createRange();
32022             }
32023         } else {
32024             return this.doc.createRange();
32025         }
32026     },
32027     getParentElement: function()
32028     {
32029         
32030         this.assignDocWin();
32031         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32032         
32033         var range = this.createRange(sel);
32034          
32035         try {
32036             var p = range.commonAncestorContainer;
32037             while (p.nodeType == 3) { // text node
32038                 p = p.parentNode;
32039             }
32040             return p;
32041         } catch (e) {
32042             return null;
32043         }
32044     
32045     },
32046     /***
32047      *
32048      * Range intersection.. the hard stuff...
32049      *  '-1' = before
32050      *  '0' = hits..
32051      *  '1' = after.
32052      *         [ -- selected range --- ]
32053      *   [fail]                        [fail]
32054      *
32055      *    basically..
32056      *      if end is before start or  hits it. fail.
32057      *      if start is after end or hits it fail.
32058      *
32059      *   if either hits (but other is outside. - then it's not 
32060      *   
32061      *    
32062      **/
32063     
32064     
32065     // @see http://www.thismuchiknow.co.uk/?p=64.
32066     rangeIntersectsNode : function(range, node)
32067     {
32068         var nodeRange = node.ownerDocument.createRange();
32069         try {
32070             nodeRange.selectNode(node);
32071         } catch (e) {
32072             nodeRange.selectNodeContents(node);
32073         }
32074     
32075         var rangeStartRange = range.cloneRange();
32076         rangeStartRange.collapse(true);
32077     
32078         var rangeEndRange = range.cloneRange();
32079         rangeEndRange.collapse(false);
32080     
32081         var nodeStartRange = nodeRange.cloneRange();
32082         nodeStartRange.collapse(true);
32083     
32084         var nodeEndRange = nodeRange.cloneRange();
32085         nodeEndRange.collapse(false);
32086     
32087         return rangeStartRange.compareBoundaryPoints(
32088                  Range.START_TO_START, nodeEndRange) == -1 &&
32089                rangeEndRange.compareBoundaryPoints(
32090                  Range.START_TO_START, nodeStartRange) == 1;
32091         
32092          
32093     },
32094     rangeCompareNode : function(range, node)
32095     {
32096         var nodeRange = node.ownerDocument.createRange();
32097         try {
32098             nodeRange.selectNode(node);
32099         } catch (e) {
32100             nodeRange.selectNodeContents(node);
32101         }
32102         
32103         
32104         range.collapse(true);
32105     
32106         nodeRange.collapse(true);
32107      
32108         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32109         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32110          
32111         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32112         
32113         var nodeIsBefore   =  ss == 1;
32114         var nodeIsAfter    = ee == -1;
32115         
32116         if (nodeIsBefore && nodeIsAfter) {
32117             return 0; // outer
32118         }
32119         if (!nodeIsBefore && nodeIsAfter) {
32120             return 1; //right trailed.
32121         }
32122         
32123         if (nodeIsBefore && !nodeIsAfter) {
32124             return 2;  // left trailed.
32125         }
32126         // fully contined.
32127         return 3;
32128     },
32129  
32130     cleanWordChars : function(input) {// change the chars to hex code
32131         
32132        var swapCodes  = [ 
32133             [    8211, "&#8211;" ], 
32134             [    8212, "&#8212;" ], 
32135             [    8216,  "'" ],  
32136             [    8217, "'" ],  
32137             [    8220, '"' ],  
32138             [    8221, '"' ],  
32139             [    8226, "*" ],  
32140             [    8230, "..." ]
32141         ]; 
32142         var output = input;
32143         Roo.each(swapCodes, function(sw) { 
32144             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32145             
32146             output = output.replace(swapper, sw[1]);
32147         });
32148         
32149         return output;
32150     },
32151     
32152      
32153     
32154         
32155     
32156     cleanUpChild : function (node)
32157     {
32158         
32159         new Roo.htmleditor.FilterComment({node : node});
32160         new Roo.htmleditor.FilterAttributes({
32161                 node : node,
32162                 attrib_black : this.ablack,
32163                 attrib_clean : this.aclean,
32164                 style_white : this.cwhite,
32165                 style_black : this.cblack
32166         });
32167         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32168         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32169          
32170         
32171     },
32172     
32173     /**
32174      * Clean up MS wordisms...
32175      * @deprecated - use filter directly
32176      */
32177     cleanWord : function(node)
32178     {
32179         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32180         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32181         
32182     },
32183    
32184     
32185     /**
32186
32187      * @deprecated - use filters
32188      */
32189     cleanTableWidths : function(node)
32190     {
32191         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32192         
32193  
32194     },
32195     
32196      
32197         
32198     applyBlacklists : function()
32199     {
32200         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32201         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32202         
32203         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32204         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32205         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32206         
32207         this.white = [];
32208         this.black = [];
32209         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32210             if (b.indexOf(tag) > -1) {
32211                 return;
32212             }
32213             this.white.push(tag);
32214             
32215         }, this);
32216         
32217         Roo.each(w, function(tag) {
32218             if (b.indexOf(tag) > -1) {
32219                 return;
32220             }
32221             if (this.white.indexOf(tag) > -1) {
32222                 return;
32223             }
32224             this.white.push(tag);
32225             
32226         }, this);
32227         
32228         
32229         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32230             if (w.indexOf(tag) > -1) {
32231                 return;
32232             }
32233             this.black.push(tag);
32234             
32235         }, this);
32236         
32237         Roo.each(b, function(tag) {
32238             if (w.indexOf(tag) > -1) {
32239                 return;
32240             }
32241             if (this.black.indexOf(tag) > -1) {
32242                 return;
32243             }
32244             this.black.push(tag);
32245             
32246         }, this);
32247         
32248         
32249         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32250         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32251         
32252         this.cwhite = [];
32253         this.cblack = [];
32254         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32255             if (b.indexOf(tag) > -1) {
32256                 return;
32257             }
32258             this.cwhite.push(tag);
32259             
32260         }, this);
32261         
32262         Roo.each(w, function(tag) {
32263             if (b.indexOf(tag) > -1) {
32264                 return;
32265             }
32266             if (this.cwhite.indexOf(tag) > -1) {
32267                 return;
32268             }
32269             this.cwhite.push(tag);
32270             
32271         }, this);
32272         
32273         
32274         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32275             if (w.indexOf(tag) > -1) {
32276                 return;
32277             }
32278             this.cblack.push(tag);
32279             
32280         }, this);
32281         
32282         Roo.each(b, function(tag) {
32283             if (w.indexOf(tag) > -1) {
32284                 return;
32285             }
32286             if (this.cblack.indexOf(tag) > -1) {
32287                 return;
32288             }
32289             this.cblack.push(tag);
32290             
32291         }, this);
32292     },
32293     
32294     setStylesheets : function(stylesheets)
32295     {
32296         if(typeof(stylesheets) == 'string'){
32297             Roo.get(this.iframe.contentDocument.head).createChild({
32298                 tag : 'link',
32299                 rel : 'stylesheet',
32300                 type : 'text/css',
32301                 href : stylesheets
32302             });
32303             
32304             return;
32305         }
32306         var _this = this;
32307      
32308         Roo.each(stylesheets, function(s) {
32309             if(!s.length){
32310                 return;
32311             }
32312             
32313             Roo.get(_this.iframe.contentDocument.head).createChild({
32314                 tag : 'link',
32315                 rel : 'stylesheet',
32316                 type : 'text/css',
32317                 href : s
32318             });
32319         });
32320
32321         
32322     },
32323     
32324     
32325     updateLanguage : function()
32326     {
32327         if (!this.iframe || !this.iframe.contentDocument) {
32328             return;
32329         }
32330         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32331     },
32332     
32333     
32334     removeStylesheets : function()
32335     {
32336         var _this = this;
32337         
32338         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32339             s.remove();
32340         });
32341     },
32342     
32343     setStyle : function(style)
32344     {
32345         Roo.get(this.iframe.contentDocument.head).createChild({
32346             tag : 'style',
32347             type : 'text/css',
32348             html : style
32349         });
32350
32351         return;
32352     }
32353     
32354     // hide stuff that is not compatible
32355     /**
32356      * @event blur
32357      * @hide
32358      */
32359     /**
32360      * @event change
32361      * @hide
32362      */
32363     /**
32364      * @event focus
32365      * @hide
32366      */
32367     /**
32368      * @event specialkey
32369      * @hide
32370      */
32371     /**
32372      * @cfg {String} fieldClass @hide
32373      */
32374     /**
32375      * @cfg {String} focusClass @hide
32376      */
32377     /**
32378      * @cfg {String} autoCreate @hide
32379      */
32380     /**
32381      * @cfg {String} inputType @hide
32382      */
32383     /**
32384      * @cfg {String} invalidClass @hide
32385      */
32386     /**
32387      * @cfg {String} invalidText @hide
32388      */
32389     /**
32390      * @cfg {String} msgFx @hide
32391      */
32392     /**
32393      * @cfg {String} validateOnBlur @hide
32394      */
32395 });
32396
32397 Roo.HtmlEditorCore.white = [
32398         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32399         
32400        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32401        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32402        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32403        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32404        'TABLE',   'UL',         'XMP', 
32405        
32406        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32407       'THEAD',   'TR', 
32408      
32409       'DIR', 'MENU', 'OL', 'UL', 'DL',
32410        
32411       'EMBED',  'OBJECT'
32412 ];
32413
32414
32415 Roo.HtmlEditorCore.black = [
32416     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32417         'APPLET', // 
32418         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32419         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32420         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32421         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32422         //'FONT' // CLEAN LATER..
32423         'COLGROUP', 'COL'   // messy tables.
32424         
32425         
32426 ];
32427 Roo.HtmlEditorCore.clean = [ // ?? needed???
32428      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32429 ];
32430 Roo.HtmlEditorCore.tag_remove = [
32431     'FONT', 'TBODY'  
32432 ];
32433 // attributes..
32434
32435 Roo.HtmlEditorCore.ablack = [
32436     'on'
32437 ];
32438     
32439 Roo.HtmlEditorCore.aclean = [ 
32440     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32441 ];
32442
32443 // protocols..
32444 Roo.HtmlEditorCore.pwhite= [
32445         'http',  'https',  'mailto'
32446 ];
32447
32448 // white listed style attributes.
32449 Roo.HtmlEditorCore.cwhite= [
32450       //  'text-align', /// default is to allow most things..
32451       
32452          
32453 //        'font-size'//??
32454 ];
32455
32456 // black listed style attributes.
32457 Roo.HtmlEditorCore.cblack= [
32458       //  'font-size' -- this can be set by the project 
32459 ];
32460
32461
32462
32463
32464     /*
32465  * - LGPL
32466  *
32467  * HtmlEditor
32468  * 
32469  */
32470
32471 /**
32472  * @class Roo.bootstrap.form.HtmlEditor
32473  * @extends Roo.bootstrap.form.TextArea
32474  * Bootstrap HtmlEditor class
32475
32476  * @constructor
32477  * Create a new HtmlEditor
32478  * @param {Object} config The config object
32479  */
32480
32481 Roo.bootstrap.form.HtmlEditor = function(config){
32482
32483     this.addEvents({
32484             /**
32485              * @event initialize
32486              * Fires when the editor is fully initialized (including the iframe)
32487              * @param {Roo.bootstrap.form.HtmlEditor} this
32488              */
32489             initialize: true,
32490             /**
32491              * @event activate
32492              * Fires when the editor is first receives the focus. Any insertion must wait
32493              * until after this event.
32494              * @param {Roo.bootstrap.form.HtmlEditor} this
32495              */
32496             activate: true,
32497              /**
32498              * @event beforesync
32499              * Fires before the textarea is updated with content from the editor iframe. Return false
32500              * to cancel the sync.
32501              * @param {Roo.bootstrap.form.HtmlEditor} this
32502              * @param {String} html
32503              */
32504             beforesync: true,
32505              /**
32506              * @event beforepush
32507              * Fires before the iframe editor is updated with content from the textarea. Return false
32508              * to cancel the push.
32509              * @param {Roo.bootstrap.form.HtmlEditor} this
32510              * @param {String} html
32511              */
32512             beforepush: true,
32513              /**
32514              * @event sync
32515              * Fires when the textarea is updated with content from the editor iframe.
32516              * @param {Roo.bootstrap.form.HtmlEditor} this
32517              * @param {String} html
32518              */
32519             sync: true,
32520              /**
32521              * @event push
32522              * Fires when the iframe editor is updated with content from the textarea.
32523              * @param {Roo.bootstrap.form.HtmlEditor} this
32524              * @param {String} html
32525              */
32526             push: true,
32527              /**
32528              * @event editmodechange
32529              * Fires when the editor switches edit modes
32530              * @param {Roo.bootstrap.form.HtmlEditor} this
32531              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32532              */
32533             editmodechange: true,
32534             /**
32535              * @event editorevent
32536              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32537              * @param {Roo.bootstrap.form.HtmlEditor} this
32538              */
32539             editorevent: true,
32540             /**
32541              * @event firstfocus
32542              * Fires when on first focus - needed by toolbars..
32543              * @param {Roo.bootstrap.form.HtmlEditor} this
32544              */
32545             firstfocus: true,
32546             /**
32547              * @event autosave
32548              * Auto save the htmlEditor value as a file into Events
32549              * @param {Roo.bootstrap.form.HtmlEditor} this
32550              */
32551             autosave: true,
32552             /**
32553              * @event savedpreview
32554              * preview the saved version of htmlEditor
32555              * @param {Roo.bootstrap.form.HtmlEditor} this
32556              */
32557             savedpreview: true,
32558              /**
32559             * @event stylesheetsclick
32560             * Fires when press the Sytlesheets button
32561             * @param {Roo.HtmlEditorCore} this
32562             */
32563             stylesheetsclick: true,
32564             /**
32565             * @event paste
32566             * Fires when press user pastes into the editor
32567             * @param {Roo.HtmlEditorCore} this
32568             */
32569             paste: true,
32570             /**
32571             * @event imageadd
32572             * Fires when on any editor when an image is added (excluding paste)
32573             * @param {Roo.bootstrap.form.HtmlEditor} this
32574             */
32575            imageadd: true ,
32576             /**
32577             * @event imageupdated
32578             * Fires when on any editor when an image is changed (excluding paste)
32579             * @param {Roo.bootstrap.form.HtmlEditor} this
32580             * @param {HTMLElement} img could also be a figure if blocks are enabled
32581             */
32582            imageupdate: true ,
32583            /**
32584             * @event imagedelete
32585             * Fires when on any editor when an image is deleted
32586             * @param {Roo.bootstrap.form.HtmlEditor} this
32587             * @param {HTMLElement} img could also be a figure if blocks are enabled
32588             */
32589            imagedelete: true  
32590     });
32591     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32592     if (!this.toolbars) {
32593         this.toolbars = [];
32594     }
32595     
32596     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32597     
32598 };
32599
32600
32601 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32602     
32603     
32604       /**
32605      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32606      */
32607     toolbars : true,
32608     
32609      /**
32610     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32611     */
32612     btns : [],
32613    
32614      /**
32615      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32616      */
32617     resize : false,
32618      /**
32619      * @cfg {Number} height (in pixels)
32620      */   
32621     height: 300,
32622    /**
32623      * @cfg {Number} width (in pixels)
32624      */   
32625     width: false,
32626     
32627     /**
32628      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32629      * 
32630      */
32631     stylesheets: false,
32632     
32633     // id of frame..
32634     frameId: false,
32635     
32636     // private properties
32637     validationEvent : false,
32638     deferHeight: true,
32639     initialized : false,
32640     activated : false,
32641     
32642     onFocus : Roo.emptyFn,
32643     iframePad:3,
32644     hideMode:'offsets',
32645     
32646     tbContainer : false,
32647     
32648     bodyCls : '',
32649     
32650     toolbarContainer :function() {
32651         return this.wrap.select('.x-html-editor-tb',true).first();
32652     },
32653
32654     /**
32655      * Protected method that will not generally be called directly. It
32656      * is called when the editor creates its toolbar. Override this method if you need to
32657      * add custom toolbar buttons.
32658      * @param {HtmlEditor} editor
32659      */
32660     createToolbar : function()
32661     {
32662         //Roo.log('renewing');
32663         //Roo.log("create toolbars");
32664         if (this.toolbars === false) {
32665             return;
32666         }
32667         if (this.toolbars === true) {
32668             this.toolbars = [ 'Standard' ];
32669         }
32670         
32671         var ar = Array.from(this.toolbars);
32672         this.toolbars = [];
32673         ar.forEach(function(t,i) {
32674             if (typeof(t) == 'string') {
32675                 t = {
32676                     xtype : t
32677                 };
32678             }
32679             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32680                 t.editor = this;
32681                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32682                 t = Roo.factory(t);
32683             }
32684             this.toolbars[i] = t;
32685             this.toolbars[i].render(this.toolbarContainer());
32686         }, this);
32687         
32688         
32689     },
32690
32691      
32692     // private
32693     onRender : function(ct, position)
32694     {
32695        // Roo.log("Call onRender: " + this.xtype);
32696         var _t = this;
32697         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32698       
32699         this.wrap = this.inputEl().wrap({
32700             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32701         });
32702         
32703         this.editorcore.onRender(ct, position);
32704          
32705          
32706         this.createToolbar(this);
32707        
32708         
32709           
32710         
32711     },
32712
32713     // private
32714     onResize : function(w, h)
32715     {
32716         Roo.log('resize: ' +w + ',' + h );
32717         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32718         var ew = false;
32719         var eh = false;
32720         
32721         if(this.inputEl() ){
32722             if(typeof w == 'number'){
32723                 var aw = w - this.wrap.getFrameWidth('lr');
32724                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32725                 ew = aw;
32726             }
32727             if(typeof h == 'number'){
32728                  var tbh = -11;  // fixme it needs to tool bar size!
32729                 for (var i =0; i < this.toolbars.length;i++) {
32730                     // fixme - ask toolbars for heights?
32731                     tbh += this.toolbars[i].el.getHeight();
32732                     //if (this.toolbars[i].footer) {
32733                     //    tbh += this.toolbars[i].footer.el.getHeight();
32734                     //}
32735                 }
32736               
32737                 
32738                 
32739                 
32740                 
32741                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32742                 ah -= 5; // knock a few pixes off for look..
32743                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32744                 var eh = ah;
32745             }
32746         }
32747         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32748         this.editorcore.onResize(ew,eh);
32749         
32750     },
32751
32752     /**
32753      * Toggles the editor between standard and source edit mode.
32754      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32755      */
32756     toggleSourceEdit : function(sourceEditMode)
32757     {
32758         this.editorcore.toggleSourceEdit(sourceEditMode);
32759         
32760         if(this.editorcore.sourceEditMode){
32761             Roo.log('editor - showing textarea');
32762             
32763 //            Roo.log('in');
32764 //            Roo.log(this.syncValue());
32765             this.syncValue();
32766             this.inputEl().removeClass(['hide', 'x-hidden']);
32767             this.inputEl().dom.removeAttribute('tabIndex');
32768             this.inputEl().focus();
32769         }else{
32770             Roo.log('editor - hiding textarea');
32771 //            Roo.log('out')
32772 //            Roo.log(this.pushValue()); 
32773             this.pushValue();
32774             
32775             this.inputEl().addClass(['hide', 'x-hidden']);
32776             this.inputEl().dom.setAttribute('tabIndex', -1);
32777             //this.deferFocus();
32778         }
32779          
32780         //if(this.resizable){
32781         //    this.setSize(this.wrap.getSize());
32782         //}
32783         
32784         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32785     },
32786  
32787     // private (for BoxComponent)
32788     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32789
32790     // private (for BoxComponent)
32791     getResizeEl : function(){
32792         return this.wrap;
32793     },
32794
32795     // private (for BoxComponent)
32796     getPositionEl : function(){
32797         return this.wrap;
32798     },
32799
32800     // private
32801     initEvents : function(){
32802         this.originalValue = this.getValue();
32803     },
32804
32805 //    /**
32806 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32807 //     * @method
32808 //     */
32809 //    markInvalid : Roo.emptyFn,
32810 //    /**
32811 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32812 //     * @method
32813 //     */
32814 //    clearInvalid : Roo.emptyFn,
32815
32816     setValue : function(v){
32817         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32818         this.editorcore.pushValue();
32819     },
32820
32821      
32822     // private
32823     deferFocus : function(){
32824         this.focus.defer(10, this);
32825     },
32826
32827     // doc'ed in Field
32828     focus : function(){
32829         this.editorcore.focus();
32830         
32831     },
32832       
32833
32834     // private
32835     onDestroy : function(){
32836         
32837         
32838         
32839         if(this.rendered){
32840             
32841             for (var i =0; i < this.toolbars.length;i++) {
32842                 // fixme - ask toolbars for heights?
32843                 this.toolbars[i].onDestroy();
32844             }
32845             
32846             this.wrap.dom.innerHTML = '';
32847             this.wrap.remove();
32848         }
32849     },
32850
32851     // private
32852     onFirstFocus : function(){
32853         //Roo.log("onFirstFocus");
32854         this.editorcore.onFirstFocus();
32855          for (var i =0; i < this.toolbars.length;i++) {
32856             this.toolbars[i].onFirstFocus();
32857         }
32858         
32859     },
32860     
32861     // private
32862     syncValue : function()
32863     {   
32864         this.editorcore.syncValue();
32865     },
32866     
32867     pushValue : function()
32868     {   
32869         this.editorcore.pushValue();
32870     }
32871      
32872     
32873     // hide stuff that is not compatible
32874     /**
32875      * @event blur
32876      * @hide
32877      */
32878     /**
32879      * @event change
32880      * @hide
32881      */
32882     /**
32883      * @event focus
32884      * @hide
32885      */
32886     /**
32887      * @event specialkey
32888      * @hide
32889      */
32890     /**
32891      * @cfg {String} fieldClass @hide
32892      */
32893     /**
32894      * @cfg {String} focusClass @hide
32895      */
32896     /**
32897      * @cfg {String} autoCreate @hide
32898      */
32899     /**
32900      * @cfg {String} inputType @hide
32901      */
32902      
32903     /**
32904      * @cfg {String} invalidText @hide
32905      */
32906     /**
32907      * @cfg {String} msgFx @hide
32908      */
32909     /**
32910      * @cfg {String} validateOnBlur @hide
32911      */
32912 });
32913  
32914     
32915    
32916    
32917    
32918       
32919 /**
32920  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32921  * @parent Roo.bootstrap.form.HtmlEditor
32922  * @extends Roo.bootstrap.nav.Simplebar
32923  * Basic Toolbar
32924  * 
32925  * @example
32926  * Usage:
32927  *
32928  new Roo.bootstrap.form.HtmlEditor({
32929     ....
32930     toolbars : [
32931         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32932             disable : { fonts: 1 , format: 1, ..., ... , ...],
32933             btns : [ .... ]
32934         })
32935     }
32936      
32937  * 
32938  * @cfg {Object} disable List of elements to disable..
32939  * @cfg {Array} btns List of additional buttons.
32940  * 
32941  * 
32942  * NEEDS Extra CSS? 
32943  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32944  */
32945  
32946 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32947 {
32948     
32949     Roo.apply(this, config);
32950     
32951     // default disabled, based on 'good practice'..
32952     this.disable = this.disable || {};
32953     Roo.applyIf(this.disable, {
32954         fontSize : true,
32955         colors : true,
32956         specialElements : true
32957     });
32958     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32959     
32960     this.editor = config.editor;
32961     this.editorcore = config.editor.editorcore;
32962     
32963     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32964     
32965     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32966     // dont call parent... till later.
32967 }
32968 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32969      
32970     bar : true,
32971     
32972     editor : false,
32973     editorcore : false,
32974     
32975     
32976     formats : [
32977         "p" ,  
32978         "h1","h2","h3","h4","h5","h6", 
32979         "pre", "code", 
32980         "abbr", "acronym", "address", "cite", "samp", "var",
32981         'div','span'
32982     ],
32983     
32984     
32985     deleteBtn: false,
32986     
32987     onRender : function(ct, position)
32988     {
32989        // Roo.log("Call onRender: " + this.xtype);
32990         
32991        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32992        Roo.log(this.el);
32993        this.el.dom.style.marginBottom = '0';
32994        var _this = this;
32995        var editorcore = this.editorcore;
32996        var editor= this.editor;
32997        
32998        var children = [];
32999        var btn = function(id, cmd , toggle, handler, html){
33000        
33001             var  event = toggle ? 'toggle' : 'click';
33002        
33003             var a = {
33004                 size : 'sm',
33005                 xtype: 'Button',
33006                 xns: Roo.bootstrap,
33007                 //glyphicon : id,
33008                 btnid : id,
33009                 fa: id,
33010                 cls : 'roo-html-editor-btn-' + id,
33011                 cmd : cmd, // why id || cmd
33012                 enableToggle: toggle !== false,
33013                 html : html || '',
33014                 pressed : toggle ? false : null,
33015                 listeners : {}
33016             };
33017             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33018                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33019             };
33020             children.push(a);
33021             return a;
33022        }
33023        
33024     //    var cb_box = function...
33025         
33026         var style = {
33027                 xtype: 'Button',
33028                 size : 'sm',
33029                 xns: Roo.bootstrap,
33030                 fa : 'font',
33031                 cls : 'roo-html-editor-font-chooser',
33032                 //html : 'submit'
33033                 menu : {
33034                     xtype: 'Menu',
33035                     xns: Roo.bootstrap,
33036                     items:  []
33037                 }
33038         };
33039         Roo.each(this.formats, function(f) {
33040             style.menu.items.push({
33041                 xtype :'MenuItem',
33042                 xns: Roo.bootstrap,
33043                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33044                 tagname : f,
33045                 listeners : {
33046                     click : function()
33047                     {
33048                         editorcore.insertTag(this.tagname);
33049                         editor.focus();
33050                     }
33051                 }
33052                 
33053             });
33054         });
33055         children.push(style);   
33056         
33057         btn('bold',         'bold',true);
33058         btn('italic',       'italic',true);
33059         btn('underline',     'underline',true);
33060         btn('align-left',   'justifyleft',true);
33061         btn('align-center', 'justifycenter',true);
33062         btn('align-right' , 'justifyright',true);
33063         btn('link', false, true, this.onLinkClick);
33064         
33065         
33066         btn('image', false, true, this.onImageClick);
33067         btn('list','insertunorderedlist',true);
33068         btn('list-ol','insertorderedlist',true);
33069
33070         btn('pencil', false,true, function(btn){
33071                 Roo.log(this);
33072                 this.toggleSourceEdit(btn.pressed);
33073         });
33074         
33075         if (this.editor.btns.length > 0) {
33076             for (var i = 0; i<this.editor.btns.length; i++) {
33077                 children.push(this.editor.btns[i]);
33078             }
33079         }
33080         
33081         
33082          
33083         this.xtype = 'NavSimplebar'; // why?
33084         
33085         for(var i=0;i< children.length;i++) {
33086             
33087             this.buttons.add(this.addxtypeChild(children[i]));
33088             
33089         }
33090         this.buildToolbarDelete();
33091
33092         editor.on('editorevent', this.updateToolbar, this);
33093     },
33094     
33095     buildToolbarDelete : function()
33096     {
33097         
33098        /* this.addxtypeChild({
33099             xtype : 'Element',
33100             xns : Roo.bootstrap,
33101             cls : 'roo-htmleditor-fill'
33102         });
33103         */
33104         this.deleteBtn = this.addxtypeChild({
33105             size : 'sm',
33106             xtype: 'Button',
33107             xns: Roo.bootstrap,
33108             fa: 'trash',
33109             listeners : {
33110                 click : this.onDelete.createDelegate(this)
33111             }
33112         });
33113         this.deleteBtn.hide();     
33114         
33115     },
33116     
33117     onImageClick : function()
33118     {
33119         if (this.input) {
33120             this.input.un('change', this.onFileSelected, this);
33121         }
33122         this.input = Roo.get(document.body).createChild({ 
33123           tag: 'input', 
33124           type : 'file', 
33125           style : 'display:none', 
33126           multiple: 'multiple'
33127        });
33128         this.input.on('change', this.onFileSelected, this);
33129         this.input.dom.click();
33130     },
33131     
33132     onFileSelected : function(e)
33133     {
33134          e.preventDefault();
33135         
33136         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33137             return;
33138         }
33139     
33140          
33141         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33142     },
33143     
33144     addFiles : function(far, fire_add) {
33145
33146          
33147         var editor =  this.editorcore;
33148   
33149         if (!far.length) {
33150             if (fire_add) {
33151                 this.editor.syncValue();
33152                 editor.owner.fireEvent('editorevent', editor.owner, false);
33153                 editor.owner.fireEvent('imageadd', editor.owner, false);
33154             }
33155             return;
33156         }
33157         
33158         var f = far.pop();
33159         
33160         if (!f.type.match(/^image/)) {
33161             this.addFiles(far, fire_add);
33162             return;
33163         }
33164          
33165         var sn = this.selectedNode;
33166         
33167         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33168         
33169         
33170         var reader = new FileReader();
33171         reader.addEventListener('load', (function() {
33172             if (bl) {
33173                 bl.image_src = reader.result;
33174                 //bl.caption = f.name;
33175                 bl.updateElement(sn);
33176                 this.editor.syncValue();
33177                 editor.owner.fireEvent('editorevent', editor.owner, false);
33178                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33179                 // we only do the first file!! and replace.
33180                 return;
33181             }
33182             if (this.editorcore.enableBlocks) {
33183                 var fig = new Roo.htmleditor.BlockFigure({
33184                     image_src :  reader.result,
33185                     caption : '',
33186                     caption_display : 'none'  //default to hide captions..
33187                  });
33188                 editor.insertAtCursor(fig.toHTML());
33189                 this.addFiles(far, true);
33190                 return;
33191             }
33192             // just a standard img..
33193             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33194                 sn.src = reader.result;
33195                 this.editor.syncValue();
33196                 editor.owner.fireEvent('editorevent', editor.owner, false);
33197                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33198                 return;
33199             }
33200             editor.insertAtCursor('<img src="' + reader.result +'">');
33201             this.addFiles(far, true);
33202             
33203         }).createDelegate(this));
33204         reader.readAsDataURL(f);
33205         
33206     
33207      },
33208     
33209     
33210     onBtnClick : function(id)
33211     {
33212        this.editorcore.relayCmd(id);
33213        this.editorcore.focus();
33214     },
33215     
33216     onLinkClick : function(btn) {
33217         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33218                 this.selectedNode.getAttribute('href') : '';
33219             
33220         Roo.bootstrap.MessageBox.show({
33221             title : "Add / Edit Link URL",
33222             msg : "Enter the URL for the link",
33223             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33224             minWidth: 250,
33225             scope : this,
33226             prompt:true,
33227             multiline: false,
33228             modal : true,
33229             value : url,
33230             fn:  function(pressed, newurl) {
33231                 if (pressed != 'ok') {
33232                     this.editorcore.focus();
33233                     return;
33234                 }
33235                 if (url != '') {
33236                     this.selectedNode.setAttribute('href', newurl);
33237                     return;
33238                 }
33239                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33240                     this.editorcore.relayCmd('createlink', newurl);
33241                 }
33242                 this.editorcore.focus();
33243             }
33244         });
33245     },
33246     /**
33247      * Protected method that will not generally be called directly. It triggers
33248      * a toolbar update by reading the markup state of the current selection in the editor.
33249      */
33250     updateToolbar: function(editor ,ev, sel){
33251
33252         if(!this.editorcore.activated){
33253             this.editor.onFirstFocus(); // is this neeed?
33254             return;
33255         }
33256
33257         var btns = this.buttons; 
33258         var doc = this.editorcore.doc;
33259         var hasToggle  = false;
33260         btns.each(function(e) {
33261             if (e.enableToggle && e.cmd) {
33262                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33263                 e.setActive(doc.queryCommandState(e.cmd));
33264             }
33265         }, this);
33266         
33267         
33268         if (ev &&
33269             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33270             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33271             // they have click on an image...
33272             // let's see if we can change the selection...
33273             sel = ev.target;
33274             
33275         }
33276         
33277         var ans = this.editorcore.getAllAncestors();
33278         if (!sel) { 
33279             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33280             sel = sel ? sel : this.editorcore.doc.body;
33281             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33282             
33283         }
33284         
33285         var lastSel = this.selectedNode;
33286         this.selectedNode = sel;
33287          
33288         // ok see if we are editing a block?
33289         
33290         var db = false;
33291         // you are not actually selecting the block.
33292         if (sel && sel.hasAttribute('data-block')) {
33293             db = sel;
33294         } else if (sel && sel.closest('[data-block]')) {
33295             db = sel.closest('[data-block]');
33296         }
33297         
33298         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33299             e.classList.remove('roo-ed-selection');
33300         });
33301         
33302         var block = false;
33303         if (db && this.editorcore.enableBlocks) {
33304             block = Roo.htmleditor.Block.factory(db);
33305             
33306             if (block) {
33307                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33308                     ' roo-ed-selection';
33309                 sel = this.selectedNode = db;
33310             }
33311         }
33312         
33313         // highlight the 'a'..
33314         var tn = sel && sel.tagName.toUpperCase() || '';
33315         if (!block && sel && tn != 'A') {
33316             var asel = sel.closest('A');
33317             if (asel) {
33318                 sel = asel;
33319             }
33320         }
33321        
33322         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33323         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33324         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33325         
33326         Roo.bootstrap.menu.Manager.hideAll();
33327          
33328         
33329         
33330         
33331         
33332         // handle delete button..
33333         if (hasToggle || (tn.length && tn == 'BODY')) {
33334             this.deleteBtn.hide();
33335             return;
33336             
33337         }
33338         this.deleteBtn.show();
33339         
33340         
33341         
33342         //this.editorsyncValue();
33343     },
33344     onFirstFocus: function() {
33345         this.buttons.each(function(item){
33346            item.enable();
33347         });
33348     },
33349     
33350     onDelete : function()
33351     {
33352         var range = this.editorcore.createRange();
33353         var selection = this.editorcore.getSelection();
33354         var sn = this.selectedNode;
33355         range.setStart(sn,0);
33356         range.setEnd(sn,0); 
33357         
33358         
33359         if (sn.hasAttribute('data-block')) {
33360             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33361             if (block) {
33362                 sn = block.removeNode();
33363                 sn.parentNode.removeChild(sn);
33364                 selection.removeAllRanges();
33365                 selection.addRange(range);
33366                 this.updateToolbar(null, null, null);
33367                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33368                     this.editor.syncValue();
33369                     this.editor.fireEvent('imagedelete', this.editor, sn);
33370                 }
33371                 
33372                 this.selectedNode = false;
33373                 this.editorcore.fireEditorEvent(false);
33374                 return;
33375             }   
33376              
33377         }
33378         if (!sn) {
33379             return; // should not really happen..
33380         }
33381         if (sn && sn.tagName == 'BODY') {
33382             return;
33383         }
33384         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33385         
33386         // remove and keep parents.
33387         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33388         a.replaceTag(sn);
33389         
33390         selection.removeAllRanges();
33391         selection.addRange(range);
33392         if (sn.tagName.toUpperCase() == 'IMG"') {
33393             this.editor.syncValue();
33394             this.editor.fireEvent('imagedelete', this.editor, sn);
33395         }
33396         
33397         this.selectedNode = false;
33398         this.editorcore.fireEditorEvent(false);
33399         
33400         
33401     },
33402     
33403     
33404     toggleSourceEdit : function(sourceEditMode){
33405         
33406           
33407         if(sourceEditMode){
33408             Roo.log("disabling buttons");
33409            this.buttons.each( function(item){
33410                 if(item.cmd != 'pencil'){
33411                     item.disable();
33412                 }
33413             });
33414           
33415         }else{
33416             Roo.log("enabling buttons");
33417             if(this.editorcore.initialized){
33418                 this.buttons.each( function(item){
33419                     item.enable();
33420                 });
33421             }
33422             
33423         }
33424         Roo.log("calling toggole on editor");
33425         // tell the editor that it's been pressed..
33426         this.editor.toggleSourceEdit(sourceEditMode);
33427        
33428     }
33429 });
33430
33431
33432
33433
33434  
33435 /*
33436  * - LGPL
33437  */
33438
33439 /**
33440  * @class Roo.bootstrap.form.Markdown
33441  * @extends Roo.bootstrap.form.TextArea
33442  * Bootstrap Showdown editable area
33443  * @cfg {string} content
33444  * 
33445  * @constructor
33446  * Create a new Showdown
33447  */
33448
33449 Roo.bootstrap.form.Markdown = function(config){
33450     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33451    
33452 };
33453
33454 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33455     
33456     editing :false,
33457     
33458     initEvents : function()
33459     {
33460         
33461         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33462         this.markdownEl = this.el.createChild({
33463             cls : 'roo-markdown-area'
33464         });
33465         this.inputEl().addClass('d-none');
33466         if (this.getValue() == '') {
33467             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33468             
33469         } else {
33470             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33471         }
33472         this.markdownEl.on('click', this.toggleTextEdit, this);
33473         this.on('blur', this.toggleTextEdit, this);
33474         this.on('specialkey', this.resizeTextArea, this);
33475     },
33476     
33477     toggleTextEdit : function()
33478     {
33479         var sh = this.markdownEl.getHeight();
33480         this.inputEl().addClass('d-none');
33481         this.markdownEl.addClass('d-none');
33482         if (!this.editing) {
33483             // show editor?
33484             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33485             this.inputEl().removeClass('d-none');
33486             this.inputEl().focus();
33487             this.editing = true;
33488             return;
33489         }
33490         // show showdown...
33491         this.updateMarkdown();
33492         this.markdownEl.removeClass('d-none');
33493         this.editing = false;
33494         return;
33495     },
33496     updateMarkdown : function()
33497     {
33498         if (this.getValue() == '') {
33499             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33500             return;
33501         }
33502  
33503         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33504     },
33505     
33506     resizeTextArea: function () {
33507         
33508         var sh = 100;
33509         Roo.log([sh, this.getValue().split("\n").length * 30]);
33510         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33511     },
33512     setValue : function(val)
33513     {
33514         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33515         if (!this.editing) {
33516             this.updateMarkdown();
33517         }
33518         
33519     },
33520     focus : function()
33521     {
33522         if (!this.editing) {
33523             this.toggleTextEdit();
33524         }
33525         
33526     }
33527
33528
33529 });/*
33530  * Based on:
33531  * Ext JS Library 1.1.1
33532  * Copyright(c) 2006-2007, Ext JS, LLC.
33533  *
33534  * Originally Released Under LGPL - original licence link has changed is not relivant.
33535  *
33536  * Fork - LGPL
33537  * <script type="text/javascript">
33538  */
33539  
33540 /**
33541  * @class Roo.bootstrap.PagingToolbar
33542  * @extends Roo.bootstrap.nav.Simplebar
33543  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33544  * @constructor
33545  * Create a new PagingToolbar
33546  * @param {Object} config The config object
33547  * @param {Roo.data.Store} store
33548  */
33549 Roo.bootstrap.PagingToolbar = function(config)
33550 {
33551     // old args format still supported... - xtype is prefered..
33552         // created from xtype...
33553     
33554     this.ds = config.dataSource;
33555     
33556     if (config.store && !this.ds) {
33557         this.store= Roo.factory(config.store, Roo.data);
33558         this.ds = this.store;
33559         this.ds.xmodule = this.xmodule || false;
33560     }
33561     
33562     this.toolbarItems = [];
33563     if (config.items) {
33564         this.toolbarItems = config.items;
33565     }
33566     
33567     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33568     
33569     this.cursor = 0;
33570     
33571     if (this.ds) { 
33572         this.bind(this.ds);
33573     }
33574     
33575     if (Roo.bootstrap.version == 4) {
33576         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33577     } else {
33578         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33579     }
33580     
33581 };
33582
33583 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33584     /**
33585      * @cfg {Roo.bootstrap.Button} buttons[]
33586      * Buttons for the toolbar
33587      */
33588      /**
33589      * @cfg {Roo.data.Store} store
33590      * The underlying data store providing the paged data
33591      */
33592     /**
33593      * @cfg {String/HTMLElement/Element} container
33594      * container The id or element that will contain the toolbar
33595      */
33596     /**
33597      * @cfg {Boolean} displayInfo
33598      * True to display the displayMsg (defaults to false)
33599      */
33600     /**
33601      * @cfg {Number} pageSize
33602      * The number of records to display per page (defaults to 20)
33603      */
33604     pageSize: 20,
33605     /**
33606      * @cfg {String} displayMsg
33607      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33608      */
33609     displayMsg : 'Displaying {0} - {1} of {2}',
33610     /**
33611      * @cfg {String} emptyMsg
33612      * The message to display when no records are found (defaults to "No data to display")
33613      */
33614     emptyMsg : 'No data to display',
33615     /**
33616      * Customizable piece of the default paging text (defaults to "Page")
33617      * @type String
33618      */
33619     beforePageText : "Page",
33620     /**
33621      * Customizable piece of the default paging text (defaults to "of %0")
33622      * @type String
33623      */
33624     afterPageText : "of {0}",
33625     /**
33626      * Customizable piece of the default paging text (defaults to "First Page")
33627      * @type String
33628      */
33629     firstText : "First Page",
33630     /**
33631      * Customizable piece of the default paging text (defaults to "Previous Page")
33632      * @type String
33633      */
33634     prevText : "Previous Page",
33635     /**
33636      * Customizable piece of the default paging text (defaults to "Next Page")
33637      * @type String
33638      */
33639     nextText : "Next Page",
33640     /**
33641      * Customizable piece of the default paging text (defaults to "Last Page")
33642      * @type String
33643      */
33644     lastText : "Last Page",
33645     /**
33646      * Customizable piece of the default paging text (defaults to "Refresh")
33647      * @type String
33648      */
33649     refreshText : "Refresh",
33650
33651     buttons : false,
33652     // private
33653     onRender : function(ct, position) 
33654     {
33655         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33656         this.navgroup.parentId = this.id;
33657         this.navgroup.onRender(this.el, null);
33658         // add the buttons to the navgroup
33659         
33660         if(this.displayInfo){
33661             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33662             this.displayEl = this.el.select('.x-paging-info', true).first();
33663 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33664 //            this.displayEl = navel.el.select('span',true).first();
33665         }
33666         
33667         var _this = this;
33668         
33669         if(this.buttons){
33670             Roo.each(_this.buttons, function(e){ // this might need to use render????
33671                Roo.factory(e).render(_this.el);
33672             });
33673         }
33674             
33675         Roo.each(_this.toolbarItems, function(e) {
33676             _this.navgroup.addItem(e);
33677         });
33678         
33679         
33680         this.first = this.navgroup.addItem({
33681             tooltip: this.firstText,
33682             cls: "prev btn-outline-secondary",
33683             html : ' <i class="fa fa-step-backward"></i>',
33684             disabled: true,
33685             preventDefault: true,
33686             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33687         });
33688         
33689         this.prev =  this.navgroup.addItem({
33690             tooltip: this.prevText,
33691             cls: "prev btn-outline-secondary",
33692             html : ' <i class="fa fa-backward"></i>',
33693             disabled: true,
33694             preventDefault: true,
33695             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33696         });
33697     //this.addSeparator();
33698         
33699         
33700         var field = this.navgroup.addItem( {
33701             tagtype : 'span',
33702             cls : 'x-paging-position  btn-outline-secondary',
33703              disabled: true,
33704             html : this.beforePageText  +
33705                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33706                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33707          } ); //?? escaped?
33708         
33709         this.field = field.el.select('input', true).first();
33710         this.field.on("keydown", this.onPagingKeydown, this);
33711         this.field.on("focus", function(){this.dom.select();});
33712     
33713     
33714         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33715         //this.field.setHeight(18);
33716         //this.addSeparator();
33717         this.next = this.navgroup.addItem({
33718             tooltip: this.nextText,
33719             cls: "next btn-outline-secondary",
33720             html : ' <i class="fa fa-forward"></i>',
33721             disabled: true,
33722             preventDefault: true,
33723             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33724         });
33725         this.last = this.navgroup.addItem({
33726             tooltip: this.lastText,
33727             html : ' <i class="fa fa-step-forward"></i>',
33728             cls: "next btn-outline-secondary",
33729             disabled: true,
33730             preventDefault: true,
33731             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33732         });
33733     //this.addSeparator();
33734         this.loading = this.navgroup.addItem({
33735             tooltip: this.refreshText,
33736             cls: "btn-outline-secondary",
33737             html : ' <i class="fa fa-refresh"></i>',
33738             preventDefault: true,
33739             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33740         });
33741         
33742     },
33743
33744     // private
33745     updateInfo : function(){
33746         if(this.displayEl){
33747             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33748             var msg = count == 0 ?
33749                 this.emptyMsg :
33750                 String.format(
33751                     this.displayMsg,
33752                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33753                 );
33754             this.displayEl.update(msg);
33755         }
33756     },
33757
33758     // private
33759     onLoad : function(ds, r, o)
33760     {
33761         this.cursor = o.params && o.params.start ? o.params.start : 0;
33762         
33763         var d = this.getPageData(),
33764             ap = d.activePage,
33765             ps = d.pages;
33766         
33767         
33768         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33769         this.field.dom.value = ap;
33770         this.first.setDisabled(ap == 1);
33771         this.prev.setDisabled(ap == 1);
33772         this.next.setDisabled(ap == ps);
33773         this.last.setDisabled(ap == ps);
33774         this.loading.enable();
33775         this.updateInfo();
33776     },
33777
33778     // private
33779     getPageData : function(){
33780         var total = this.ds.getTotalCount();
33781         return {
33782             total : total,
33783             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33784             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33785         };
33786     },
33787
33788     // private
33789     onLoadError : function(proxy, o){
33790         this.loading.enable();
33791         if (this.ds.events.loadexception.listeners.length  < 2) {
33792             // nothing has been assigned to loadexception except this...
33793             // so 
33794             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33795
33796         }
33797     },
33798
33799     // private
33800     onPagingKeydown : function(e){
33801         var k = e.getKey();
33802         var d = this.getPageData();
33803         if(k == e.RETURN){
33804             var v = this.field.dom.value, pageNum;
33805             if(!v || isNaN(pageNum = parseInt(v, 10))){
33806                 this.field.dom.value = d.activePage;
33807                 return;
33808             }
33809             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33810             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33811             e.stopEvent();
33812         }
33813         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))
33814         {
33815           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33816           this.field.dom.value = pageNum;
33817           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33818           e.stopEvent();
33819         }
33820         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33821         {
33822           var v = this.field.dom.value, pageNum; 
33823           var increment = (e.shiftKey) ? 10 : 1;
33824           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33825                 increment *= -1;
33826           }
33827           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33828             this.field.dom.value = d.activePage;
33829             return;
33830           }
33831           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33832           {
33833             this.field.dom.value = parseInt(v, 10) + increment;
33834             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33835             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33836           }
33837           e.stopEvent();
33838         }
33839     },
33840
33841     // private
33842     beforeLoad : function(){
33843         if(this.loading){
33844             this.loading.disable();
33845         }
33846     },
33847
33848     // private
33849     onClick : function(which){
33850         
33851         var ds = this.ds;
33852         if (!ds) {
33853             return;
33854         }
33855         
33856         switch(which){
33857             case "first":
33858                 ds.load({params:{start: 0, limit: this.pageSize}});
33859             break;
33860             case "prev":
33861                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33862             break;
33863             case "next":
33864                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33865             break;
33866             case "last":
33867                 var total = ds.getTotalCount();
33868                 var extra = total % this.pageSize;
33869                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33870                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33871             break;
33872             case "refresh":
33873                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33874             break;
33875         }
33876     },
33877
33878     /**
33879      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33880      * @param {Roo.data.Store} store The data store to unbind
33881      */
33882     unbind : function(ds){
33883         ds.un("beforeload", this.beforeLoad, this);
33884         ds.un("load", this.onLoad, this);
33885         ds.un("loadexception", this.onLoadError, this);
33886         ds.un("remove", this.updateInfo, this);
33887         ds.un("add", this.updateInfo, this);
33888         this.ds = undefined;
33889     },
33890
33891     /**
33892      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33893      * @param {Roo.data.Store} store The data store to bind
33894      */
33895     bind : function(ds){
33896         ds.on("beforeload", this.beforeLoad, this);
33897         ds.on("load", this.onLoad, this);
33898         ds.on("loadexception", this.onLoadError, this);
33899         ds.on("remove", this.updateInfo, this);
33900         ds.on("add", this.updateInfo, this);
33901         this.ds = ds;
33902     }
33903 });/*
33904  * - LGPL
33905  *
33906  * element
33907  * 
33908  */
33909
33910 /**
33911  * @class Roo.bootstrap.MessageBar
33912  * @extends Roo.bootstrap.Component
33913  * Bootstrap MessageBar class
33914  * @cfg {String} html contents of the MessageBar
33915  * @cfg {String} weight (info | success | warning | danger) default info
33916  * @cfg {String} beforeClass insert the bar before the given class
33917  * @cfg {Boolean} closable (true | false) default false
33918  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33919  * 
33920  * @constructor
33921  * Create a new Element
33922  * @param {Object} config The config object
33923  */
33924
33925 Roo.bootstrap.MessageBar = function(config){
33926     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33927 };
33928
33929 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33930     
33931     html: '',
33932     weight: 'info',
33933     closable: false,
33934     fixed: false,
33935     beforeClass: 'bootstrap-sticky-wrap',
33936     
33937     getAutoCreate : function(){
33938         
33939         var cfg = {
33940             tag: 'div',
33941             cls: 'alert alert-dismissable alert-' + this.weight,
33942             cn: [
33943                 {
33944                     tag: 'span',
33945                     cls: 'message',
33946                     html: this.html || ''
33947                 }
33948             ]
33949         };
33950         
33951         if(this.fixed){
33952             cfg.cls += ' alert-messages-fixed';
33953         }
33954         
33955         if(this.closable){
33956             cfg.cn.push({
33957                 tag: 'button',
33958                 cls: 'close',
33959                 html: 'x'
33960             });
33961         }
33962         
33963         return cfg;
33964     },
33965     
33966     onRender : function(ct, position)
33967     {
33968         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33969         
33970         if(!this.el){
33971             var cfg = Roo.apply({},  this.getAutoCreate());
33972             cfg.id = Roo.id();
33973             
33974             if (this.cls) {
33975                 cfg.cls += ' ' + this.cls;
33976             }
33977             if (this.style) {
33978                 cfg.style = this.style;
33979             }
33980             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33981             
33982             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33983         }
33984         
33985         this.el.select('>button.close').on('click', this.hide, this);
33986         
33987     },
33988     
33989     show : function()
33990     {
33991         if (!this.rendered) {
33992             this.render();
33993         }
33994         
33995         this.el.show();
33996         
33997         this.fireEvent('show', this);
33998         
33999     },
34000     
34001     hide : function()
34002     {
34003         if (!this.rendered) {
34004             this.render();
34005         }
34006         
34007         this.el.hide();
34008         
34009         this.fireEvent('hide', this);
34010     },
34011     
34012     update : function()
34013     {
34014 //        var e = this.el.dom.firstChild;
34015 //        
34016 //        if(this.closable){
34017 //            e = e.nextSibling;
34018 //        }
34019 //        
34020 //        e.data = this.html || '';
34021
34022         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34023     }
34024    
34025 });
34026
34027  
34028
34029      /*
34030  * - LGPL
34031  *
34032  * Graph
34033  * 
34034  */
34035
34036
34037 /**
34038  * @class Roo.bootstrap.Graph
34039  * @extends Roo.bootstrap.Component
34040  * Bootstrap Graph class
34041 > Prameters
34042  -sm {number} sm 4
34043  -md {number} md 5
34044  @cfg {String} graphtype  bar | vbar | pie
34045  @cfg {number} g_x coodinator | centre x (pie)
34046  @cfg {number} g_y coodinator | centre y (pie)
34047  @cfg {number} g_r radius (pie)
34048  @cfg {number} g_height height of the chart (respected by all elements in the set)
34049  @cfg {number} g_width width of the chart (respected by all elements in the set)
34050  @cfg {Object} title The title of the chart
34051     
34052  -{Array}  values
34053  -opts (object) options for the chart 
34054      o {
34055      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34056      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34057      o vgutter (number)
34058      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.
34059      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34060      o to
34061      o stretch (boolean)
34062      o }
34063  -opts (object) options for the pie
34064      o{
34065      o cut
34066      o startAngle (number)
34067      o endAngle (number)
34068      } 
34069  *
34070  * @constructor
34071  * Create a new Input
34072  * @param {Object} config The config object
34073  */
34074
34075 Roo.bootstrap.Graph = function(config){
34076     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34077     
34078     this.addEvents({
34079         // img events
34080         /**
34081          * @event click
34082          * The img click event for the img.
34083          * @param {Roo.EventObject} e
34084          */
34085         "click" : true
34086     });
34087 };
34088
34089 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34090     
34091     sm: 4,
34092     md: 5,
34093     graphtype: 'bar',
34094     g_height: 250,
34095     g_width: 400,
34096     g_x: 50,
34097     g_y: 50,
34098     g_r: 30,
34099     opts:{
34100         //g_colors: this.colors,
34101         g_type: 'soft',
34102         g_gutter: '20%'
34103
34104     },
34105     title : false,
34106
34107     getAutoCreate : function(){
34108         
34109         var cfg = {
34110             tag: 'div',
34111             html : null
34112         };
34113         
34114         
34115         return  cfg;
34116     },
34117
34118     onRender : function(ct,position){
34119         
34120         
34121         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34122         
34123         if (typeof(Raphael) == 'undefined') {
34124             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34125             return;
34126         }
34127         
34128         this.raphael = Raphael(this.el.dom);
34129         
34130                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34131                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34132                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34133                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34134                 /*
34135                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34136                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34137                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34138                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34139                 
34140                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34141                 r.barchart(330, 10, 300, 220, data1);
34142                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34143                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34144                 */
34145                 
34146                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34147                 // r.barchart(30, 30, 560, 250,  xdata, {
34148                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34149                 //     axis : "0 0 1 1",
34150                 //     axisxlabels :  xdata
34151                 //     //yvalues : cols,
34152                    
34153                 // });
34154 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34155 //        
34156 //        this.load(null,xdata,{
34157 //                axis : "0 0 1 1",
34158 //                axisxlabels :  xdata
34159 //                });
34160
34161     },
34162
34163     load : function(graphtype,xdata,opts)
34164     {
34165         this.raphael.clear();
34166         if(!graphtype) {
34167             graphtype = this.graphtype;
34168         }
34169         if(!opts){
34170             opts = this.opts;
34171         }
34172         var r = this.raphael,
34173             fin = function () {
34174                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34175             },
34176             fout = function () {
34177                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34178             },
34179             pfin = function() {
34180                 this.sector.stop();
34181                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34182
34183                 if (this.label) {
34184                     this.label[0].stop();
34185                     this.label[0].attr({ r: 7.5 });
34186                     this.label[1].attr({ "font-weight": 800 });
34187                 }
34188             },
34189             pfout = function() {
34190                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34191
34192                 if (this.label) {
34193                     this.label[0].animate({ r: 5 }, 500, "bounce");
34194                     this.label[1].attr({ "font-weight": 400 });
34195                 }
34196             };
34197
34198         switch(graphtype){
34199             case 'bar':
34200                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34201                 break;
34202             case 'hbar':
34203                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34204                 break;
34205             case 'pie':
34206 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34207 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34208 //            
34209                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34210                 
34211                 break;
34212
34213         }
34214         
34215         if(this.title){
34216             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34217         }
34218         
34219     },
34220     
34221     setTitle: function(o)
34222     {
34223         this.title = o;
34224     },
34225     
34226     initEvents: function() {
34227         
34228         if(!this.href){
34229             this.el.on('click', this.onClick, this);
34230         }
34231     },
34232     
34233     onClick : function(e)
34234     {
34235         Roo.log('img onclick');
34236         this.fireEvent('click', this, e);
34237     }
34238    
34239 });
34240
34241  
34242 Roo.bootstrap.dash = {};/*
34243  * - LGPL
34244  *
34245  * numberBox
34246  * 
34247  */
34248 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34249
34250 /**
34251  * @class Roo.bootstrap.dash.NumberBox
34252  * @extends Roo.bootstrap.Component
34253  * Bootstrap NumberBox class
34254  * @cfg {String} headline Box headline
34255  * @cfg {String} content Box content
34256  * @cfg {String} icon Box icon
34257  * @cfg {String} footer Footer text
34258  * @cfg {String} fhref Footer href
34259  * 
34260  * @constructor
34261  * Create a new NumberBox
34262  * @param {Object} config The config object
34263  */
34264
34265
34266 Roo.bootstrap.dash.NumberBox = function(config){
34267     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34268     
34269 };
34270
34271 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34272     
34273     headline : '',
34274     content : '',
34275     icon : '',
34276     footer : '',
34277     fhref : '',
34278     ficon : '',
34279     
34280     getAutoCreate : function(){
34281         
34282         var cfg = {
34283             tag : 'div',
34284             cls : 'small-box ',
34285             cn : [
34286                 {
34287                     tag : 'div',
34288                     cls : 'inner',
34289                     cn :[
34290                         {
34291                             tag : 'h3',
34292                             cls : 'roo-headline',
34293                             html : this.headline
34294                         },
34295                         {
34296                             tag : 'p',
34297                             cls : 'roo-content',
34298                             html : this.content
34299                         }
34300                     ]
34301                 }
34302             ]
34303         };
34304         
34305         if(this.icon){
34306             cfg.cn.push({
34307                 tag : 'div',
34308                 cls : 'icon',
34309                 cn :[
34310                     {
34311                         tag : 'i',
34312                         cls : 'ion ' + this.icon
34313                     }
34314                 ]
34315             });
34316         }
34317         
34318         if(this.footer){
34319             var footer = {
34320                 tag : 'a',
34321                 cls : 'small-box-footer',
34322                 href : this.fhref || '#',
34323                 html : this.footer
34324             };
34325             
34326             cfg.cn.push(footer);
34327             
34328         }
34329         
34330         return  cfg;
34331     },
34332
34333     onRender : function(ct,position){
34334         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34335
34336
34337        
34338                 
34339     },
34340
34341     setHeadline: function (value)
34342     {
34343         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34344     },
34345     
34346     setFooter: function (value, href)
34347     {
34348         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34349         
34350         if(href){
34351             this.el.select('a.small-box-footer',true).first().attr('href', href);
34352         }
34353         
34354     },
34355
34356     setContent: function (value)
34357     {
34358         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34359     },
34360
34361     initEvents: function() 
34362     {   
34363         
34364     }
34365     
34366 });
34367
34368  
34369 /*
34370  * - LGPL
34371  *
34372  * TabBox
34373  * 
34374  */
34375 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34376
34377 /**
34378  * @class Roo.bootstrap.dash.TabBox
34379  * @extends Roo.bootstrap.Component
34380  * @children Roo.bootstrap.dash.TabPane
34381  * Bootstrap TabBox class
34382  * @cfg {String} title Title of the TabBox
34383  * @cfg {String} icon Icon of the TabBox
34384  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34385  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34386  * 
34387  * @constructor
34388  * Create a new TabBox
34389  * @param {Object} config The config object
34390  */
34391
34392
34393 Roo.bootstrap.dash.TabBox = function(config){
34394     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34395     this.addEvents({
34396         // raw events
34397         /**
34398          * @event addpane
34399          * When a pane is added
34400          * @param {Roo.bootstrap.dash.TabPane} pane
34401          */
34402         "addpane" : true,
34403         /**
34404          * @event activatepane
34405          * When a pane is activated
34406          * @param {Roo.bootstrap.dash.TabPane} pane
34407          */
34408         "activatepane" : true
34409         
34410          
34411     });
34412     
34413     this.panes = [];
34414 };
34415
34416 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34417
34418     title : '',
34419     icon : false,
34420     showtabs : true,
34421     tabScrollable : false,
34422     
34423     getChildContainer : function()
34424     {
34425         return this.el.select('.tab-content', true).first();
34426     },
34427     
34428     getAutoCreate : function(){
34429         
34430         var header = {
34431             tag: 'li',
34432             cls: 'pull-left header',
34433             html: this.title,
34434             cn : []
34435         };
34436         
34437         if(this.icon){
34438             header.cn.push({
34439                 tag: 'i',
34440                 cls: 'fa ' + this.icon
34441             });
34442         }
34443         
34444         var h = {
34445             tag: 'ul',
34446             cls: 'nav nav-tabs pull-right',
34447             cn: [
34448                 header
34449             ]
34450         };
34451         
34452         if(this.tabScrollable){
34453             h = {
34454                 tag: 'div',
34455                 cls: 'tab-header',
34456                 cn: [
34457                     {
34458                         tag: 'ul',
34459                         cls: 'nav nav-tabs pull-right',
34460                         cn: [
34461                             header
34462                         ]
34463                     }
34464                 ]
34465             };
34466         }
34467         
34468         var cfg = {
34469             tag: 'div',
34470             cls: 'nav-tabs-custom',
34471             cn: [
34472                 h,
34473                 {
34474                     tag: 'div',
34475                     cls: 'tab-content no-padding',
34476                     cn: []
34477                 }
34478             ]
34479         };
34480
34481         return  cfg;
34482     },
34483     initEvents : function()
34484     {
34485         //Roo.log('add add pane handler');
34486         this.on('addpane', this.onAddPane, this);
34487     },
34488      /**
34489      * Updates the box title
34490      * @param {String} html to set the title to.
34491      */
34492     setTitle : function(value)
34493     {
34494         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34495     },
34496     onAddPane : function(pane)
34497     {
34498         this.panes.push(pane);
34499         //Roo.log('addpane');
34500         //Roo.log(pane);
34501         // tabs are rendere left to right..
34502         if(!this.showtabs){
34503             return;
34504         }
34505         
34506         var ctr = this.el.select('.nav-tabs', true).first();
34507          
34508          
34509         var existing = ctr.select('.nav-tab',true);
34510         var qty = existing.getCount();;
34511         
34512         
34513         var tab = ctr.createChild({
34514             tag : 'li',
34515             cls : 'nav-tab' + (qty ? '' : ' active'),
34516             cn : [
34517                 {
34518                     tag : 'a',
34519                     href:'#',
34520                     html : pane.title
34521                 }
34522             ]
34523         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34524         pane.tab = tab;
34525         
34526         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34527         if (!qty) {
34528             pane.el.addClass('active');
34529         }
34530         
34531                 
34532     },
34533     onTabClick : function(ev,un,ob,pane)
34534     {
34535         //Roo.log('tab - prev default');
34536         ev.preventDefault();
34537         
34538         
34539         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34540         pane.tab.addClass('active');
34541         //Roo.log(pane.title);
34542         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34543         // technically we should have a deactivate event.. but maybe add later.
34544         // and it should not de-activate the selected tab...
34545         this.fireEvent('activatepane', pane);
34546         pane.el.addClass('active');
34547         pane.fireEvent('activate');
34548         
34549         
34550     },
34551     
34552     getActivePane : function()
34553     {
34554         var r = false;
34555         Roo.each(this.panes, function(p) {
34556             if(p.el.hasClass('active')){
34557                 r = p;
34558                 return false;
34559             }
34560             
34561             return;
34562         });
34563         
34564         return r;
34565     }
34566     
34567     
34568 });
34569
34570  
34571 /*
34572  * - LGPL
34573  *
34574  * Tab pane
34575  * 
34576  */
34577 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34578 /**
34579  * @class Roo.bootstrap.TabPane
34580  * @extends Roo.bootstrap.Component
34581  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34582  * Bootstrap TabPane class
34583  * @cfg {Boolean} active (false | true) Default false
34584  * @cfg {String} title title of panel
34585
34586  * 
34587  * @constructor
34588  * Create a new TabPane
34589  * @param {Object} config The config object
34590  */
34591
34592 Roo.bootstrap.dash.TabPane = function(config){
34593     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34594     
34595     this.addEvents({
34596         // raw events
34597         /**
34598          * @event activate
34599          * When a pane is activated
34600          * @param {Roo.bootstrap.dash.TabPane} pane
34601          */
34602         "activate" : true
34603          
34604     });
34605 };
34606
34607 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34608     
34609     active : false,
34610     title : '',
34611     
34612     // the tabBox that this is attached to.
34613     tab : false,
34614      
34615     getAutoCreate : function() 
34616     {
34617         var cfg = {
34618             tag: 'div',
34619             cls: 'tab-pane'
34620         };
34621         
34622         if(this.active){
34623             cfg.cls += ' active';
34624         }
34625         
34626         return cfg;
34627     },
34628     initEvents  : function()
34629     {
34630         //Roo.log('trigger add pane handler');
34631         this.parent().fireEvent('addpane', this)
34632     },
34633     
34634      /**
34635      * Updates the tab title 
34636      * @param {String} html to set the title to.
34637      */
34638     setTitle: function(str)
34639     {
34640         if (!this.tab) {
34641             return;
34642         }
34643         this.title = str;
34644         this.tab.select('a', true).first().dom.innerHTML = str;
34645         
34646     }
34647     
34648     
34649     
34650 });
34651
34652  
34653
34654
34655  /*
34656  * - LGPL
34657  *
34658  * Tooltip
34659  * 
34660  */
34661
34662 /**
34663  * @class Roo.bootstrap.Tooltip
34664  * Bootstrap Tooltip class
34665  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34666  * to determine which dom element triggers the tooltip.
34667  * 
34668  * It needs to add support for additional attributes like tooltip-position
34669  * 
34670  * @constructor
34671  * Create a new Toolti
34672  * @param {Object} config The config object
34673  */
34674
34675 Roo.bootstrap.Tooltip = function(config){
34676     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34677     
34678     this.alignment = Roo.bootstrap.Tooltip.alignment;
34679     
34680     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34681         this.alignment = config.alignment;
34682     }
34683     
34684 };
34685
34686 Roo.apply(Roo.bootstrap.Tooltip, {
34687     /**
34688      * @function init initialize tooltip monitoring.
34689      * @static
34690      */
34691     currentEl : false,
34692     currentTip : false,
34693     currentRegion : false,
34694     
34695     //  init : delay?
34696     
34697     init : function()
34698     {
34699         Roo.get(document).on('mouseover', this.enter ,this);
34700         Roo.get(document).on('mouseout', this.leave, this);
34701          
34702         
34703         this.currentTip = new Roo.bootstrap.Tooltip();
34704     },
34705     
34706     enter : function(ev)
34707     {
34708         var dom = ev.getTarget();
34709         
34710         //Roo.log(['enter',dom]);
34711         var el = Roo.fly(dom);
34712         if (this.currentEl) {
34713             //Roo.log(dom);
34714             //Roo.log(this.currentEl);
34715             //Roo.log(this.currentEl.contains(dom));
34716             if (this.currentEl == el) {
34717                 return;
34718             }
34719             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34720                 return;
34721             }
34722
34723         }
34724         
34725         if (this.currentTip.el) {
34726             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34727         }    
34728         //Roo.log(ev);
34729         
34730         if(!el || el.dom == document){
34731             return;
34732         }
34733         
34734         var bindEl = el; 
34735         var pel = false;
34736         if (!el.attr('tooltip')) {
34737             pel = el.findParent("[tooltip]");
34738             if (pel) {
34739                 bindEl = Roo.get(pel);
34740             }
34741         }
34742         
34743        
34744         
34745         // you can not look for children, as if el is the body.. then everythign is the child..
34746         if (!pel && !el.attr('tooltip')) { //
34747             if (!el.select("[tooltip]").elements.length) {
34748                 return;
34749             }
34750             // is the mouse over this child...?
34751             bindEl = el.select("[tooltip]").first();
34752             var xy = ev.getXY();
34753             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34754                 //Roo.log("not in region.");
34755                 return;
34756             }
34757             //Roo.log("child element over..");
34758             
34759         }
34760         this.currentEl = el;
34761         this.currentTip.bind(bindEl);
34762         this.currentRegion = Roo.lib.Region.getRegion(dom);
34763         this.currentTip.enter();
34764         
34765     },
34766     leave : function(ev)
34767     {
34768         var dom = ev.getTarget();
34769         //Roo.log(['leave',dom]);
34770         if (!this.currentEl) {
34771             return;
34772         }
34773         
34774         
34775         if (dom != this.currentEl.dom) {
34776             return;
34777         }
34778         var xy = ev.getXY();
34779         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34780             return;
34781         }
34782         // only activate leave if mouse cursor is outside... bounding box..
34783         
34784         
34785         
34786         
34787         if (this.currentTip) {
34788             this.currentTip.leave();
34789         }
34790         //Roo.log('clear currentEl');
34791         this.currentEl = false;
34792         
34793         
34794     },
34795     alignment : {
34796         'left' : ['r-l', [-2,0], 'right'],
34797         'right' : ['l-r', [2,0], 'left'],
34798         'bottom' : ['t-b', [0,2], 'top'],
34799         'top' : [ 'b-t', [0,-2], 'bottom']
34800     }
34801     
34802 });
34803
34804
34805 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34806     
34807     
34808     bindEl : false,
34809     
34810     delay : null, // can be { show : 300 , hide: 500}
34811     
34812     timeout : null,
34813     
34814     hoverState : null, //???
34815     
34816     placement : 'bottom', 
34817     
34818     alignment : false,
34819     
34820     getAutoCreate : function(){
34821     
34822         var cfg = {
34823            cls : 'tooltip',   
34824            role : 'tooltip',
34825            cn : [
34826                 {
34827                     cls : 'tooltip-arrow arrow'
34828                 },
34829                 {
34830                     cls : 'tooltip-inner'
34831                 }
34832            ]
34833         };
34834         
34835         return cfg;
34836     },
34837     bind : function(el)
34838     {
34839         this.bindEl = el;
34840     },
34841     
34842     initEvents : function()
34843     {
34844         this.arrowEl = this.el.select('.arrow', true).first();
34845         this.innerEl = this.el.select('.tooltip-inner', true).first();
34846     },
34847     
34848     enter : function () {
34849        
34850         if (this.timeout != null) {
34851             clearTimeout(this.timeout);
34852         }
34853         
34854         this.hoverState = 'in';
34855          //Roo.log("enter - show");
34856         if (!this.delay || !this.delay.show) {
34857             this.show();
34858             return;
34859         }
34860         var _t = this;
34861         this.timeout = setTimeout(function () {
34862             if (_t.hoverState == 'in') {
34863                 _t.show();
34864             }
34865         }, this.delay.show);
34866     },
34867     leave : function()
34868     {
34869         clearTimeout(this.timeout);
34870     
34871         this.hoverState = 'out';
34872          if (!this.delay || !this.delay.hide) {
34873             this.hide();
34874             return;
34875         }
34876        
34877         var _t = this;
34878         this.timeout = setTimeout(function () {
34879             //Roo.log("leave - timeout");
34880             
34881             if (_t.hoverState == 'out') {
34882                 _t.hide();
34883                 Roo.bootstrap.Tooltip.currentEl = false;
34884             }
34885         }, delay);
34886     },
34887     
34888     show : function (msg)
34889     {
34890         if (!this.el) {
34891             this.render(document.body);
34892         }
34893         // set content.
34894         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34895         
34896         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34897         
34898         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34899         
34900         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34901                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34902
34903         if(this.bindEl.attr('tooltip-class')) {
34904             this.el.addClass(this.bindEl.attr('tooltip-class'));
34905         }
34906         
34907         var placement = typeof this.placement == 'function' ?
34908             this.placement.call(this, this.el, on_el) :
34909             this.placement;
34910         
34911         if(this.bindEl.attr('tooltip-placement')) {
34912             placement = this.bindEl.attr('tooltip-placement');
34913         }
34914             
34915         var autoToken = /\s?auto?\s?/i;
34916         var autoPlace = autoToken.test(placement);
34917         if (autoPlace) {
34918             placement = placement.replace(autoToken, '') || 'top';
34919         }
34920         
34921         //this.el.detach()
34922         //this.el.setXY([0,0]);
34923         this.el.show();
34924         //this.el.dom.style.display='block';
34925         
34926         //this.el.appendTo(on_el);
34927         
34928         var p = this.getPosition();
34929         var box = this.el.getBox();
34930         
34931         if (autoPlace) {
34932             // fixme..
34933         }
34934         
34935         var align = this.alignment[placement];
34936         
34937         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34938         
34939         if(placement == 'top' || placement == 'bottom'){
34940             if(xy[0] < 0){
34941                 placement = 'right';
34942             }
34943             
34944             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34945                 placement = 'left';
34946             }
34947             
34948             var scroll = Roo.select('body', true).first().getScroll();
34949             
34950             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34951                 placement = 'top';
34952             }
34953             
34954             align = this.alignment[placement];
34955             
34956             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34957             
34958         }
34959         
34960         var elems = document.getElementsByTagName('div');
34961         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34962         for (var i = 0; i < elems.length; i++) {
34963           var zindex = Number.parseInt(
34964                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34965                 10
34966           );
34967           if (zindex > highest) {
34968             highest = zindex;
34969           }
34970         }
34971         
34972         
34973         
34974         this.el.dom.style.zIndex = highest;
34975         
34976         this.el.alignTo(this.bindEl, align[0],align[1]);
34977         //var arrow = this.el.select('.arrow',true).first();
34978         //arrow.set(align[2], 
34979         
34980         this.el.addClass(placement);
34981         this.el.addClass("bs-tooltip-"+ placement);
34982         
34983         this.el.addClass('in fade show');
34984         
34985         this.hoverState = null;
34986         
34987         if (this.el.hasClass('fade')) {
34988             // fade it?
34989         }
34990         
34991         
34992         
34993         
34994         
34995     },
34996     hide : function()
34997     {
34998          
34999         if (!this.el) {
35000             return;
35001         }
35002         //this.el.setXY([0,0]);
35003         if(this.bindEl.attr('tooltip-class')) {
35004             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35005         }
35006         this.el.removeClass(['show', 'in']);
35007         //this.el.hide();
35008         
35009     }
35010     
35011 });
35012  
35013
35014  /*
35015  * - LGPL
35016  *
35017  * Location Picker
35018  * 
35019  */
35020
35021 /**
35022  * @class Roo.bootstrap.LocationPicker
35023  * @extends Roo.bootstrap.Component
35024  * Bootstrap LocationPicker class
35025  * @cfg {Number} latitude Position when init default 0
35026  * @cfg {Number} longitude Position when init default 0
35027  * @cfg {Number} zoom default 15
35028  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35029  * @cfg {Boolean} mapTypeControl default false
35030  * @cfg {Boolean} disableDoubleClickZoom default false
35031  * @cfg {Boolean} scrollwheel default true
35032  * @cfg {Boolean} streetViewControl default false
35033  * @cfg {Number} radius default 0
35034  * @cfg {String} locationName
35035  * @cfg {Boolean} draggable default true
35036  * @cfg {Boolean} enableAutocomplete default false
35037  * @cfg {Boolean} enableReverseGeocode default true
35038  * @cfg {String} markerTitle
35039  * 
35040  * @constructor
35041  * Create a new LocationPicker
35042  * @param {Object} config The config object
35043  */
35044
35045
35046 Roo.bootstrap.LocationPicker = function(config){
35047     
35048     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35049     
35050     this.addEvents({
35051         /**
35052          * @event initial
35053          * Fires when the picker initialized.
35054          * @param {Roo.bootstrap.LocationPicker} this
35055          * @param {Google Location} location
35056          */
35057         initial : true,
35058         /**
35059          * @event positionchanged
35060          * Fires when the picker position changed.
35061          * @param {Roo.bootstrap.LocationPicker} this
35062          * @param {Google Location} location
35063          */
35064         positionchanged : true,
35065         /**
35066          * @event resize
35067          * Fires when the map resize.
35068          * @param {Roo.bootstrap.LocationPicker} this
35069          */
35070         resize : true,
35071         /**
35072          * @event show
35073          * Fires when the map show.
35074          * @param {Roo.bootstrap.LocationPicker} this
35075          */
35076         show : true,
35077         /**
35078          * @event hide
35079          * Fires when the map hide.
35080          * @param {Roo.bootstrap.LocationPicker} this
35081          */
35082         hide : true,
35083         /**
35084          * @event mapClick
35085          * Fires when click the map.
35086          * @param {Roo.bootstrap.LocationPicker} this
35087          * @param {Map event} e
35088          */
35089         mapClick : true,
35090         /**
35091          * @event mapRightClick
35092          * Fires when right click the map.
35093          * @param {Roo.bootstrap.LocationPicker} this
35094          * @param {Map event} e
35095          */
35096         mapRightClick : true,
35097         /**
35098          * @event markerClick
35099          * Fires when click the marker.
35100          * @param {Roo.bootstrap.LocationPicker} this
35101          * @param {Map event} e
35102          */
35103         markerClick : true,
35104         /**
35105          * @event markerRightClick
35106          * Fires when right click the marker.
35107          * @param {Roo.bootstrap.LocationPicker} this
35108          * @param {Map event} e
35109          */
35110         markerRightClick : true,
35111         /**
35112          * @event OverlayViewDraw
35113          * Fires when OverlayView Draw
35114          * @param {Roo.bootstrap.LocationPicker} this
35115          */
35116         OverlayViewDraw : true,
35117         /**
35118          * @event OverlayViewOnAdd
35119          * Fires when OverlayView Draw
35120          * @param {Roo.bootstrap.LocationPicker} this
35121          */
35122         OverlayViewOnAdd : true,
35123         /**
35124          * @event OverlayViewOnRemove
35125          * Fires when OverlayView Draw
35126          * @param {Roo.bootstrap.LocationPicker} this
35127          */
35128         OverlayViewOnRemove : true,
35129         /**
35130          * @event OverlayViewShow
35131          * Fires when OverlayView Draw
35132          * @param {Roo.bootstrap.LocationPicker} this
35133          * @param {Pixel} cpx
35134          */
35135         OverlayViewShow : true,
35136         /**
35137          * @event OverlayViewHide
35138          * Fires when OverlayView Draw
35139          * @param {Roo.bootstrap.LocationPicker} this
35140          */
35141         OverlayViewHide : true,
35142         /**
35143          * @event loadexception
35144          * Fires when load google lib failed.
35145          * @param {Roo.bootstrap.LocationPicker} this
35146          */
35147         loadexception : true
35148     });
35149         
35150 };
35151
35152 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35153     
35154     gMapContext: false,
35155     
35156     latitude: 0,
35157     longitude: 0,
35158     zoom: 15,
35159     mapTypeId: false,
35160     mapTypeControl: false,
35161     disableDoubleClickZoom: false,
35162     scrollwheel: true,
35163     streetViewControl: false,
35164     radius: 0,
35165     locationName: '',
35166     draggable: true,
35167     enableAutocomplete: false,
35168     enableReverseGeocode: true,
35169     markerTitle: '',
35170     
35171     getAutoCreate: function()
35172     {
35173
35174         var cfg = {
35175             tag: 'div',
35176             cls: 'roo-location-picker'
35177         };
35178         
35179         return cfg
35180     },
35181     
35182     initEvents: function(ct, position)
35183     {       
35184         if(!this.el.getWidth() || this.isApplied()){
35185             return;
35186         }
35187         
35188         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35189         
35190         this.initial();
35191     },
35192     
35193     initial: function()
35194     {
35195         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35196             this.fireEvent('loadexception', this);
35197             return;
35198         }
35199         
35200         if(!this.mapTypeId){
35201             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35202         }
35203         
35204         this.gMapContext = this.GMapContext();
35205         
35206         this.initOverlayView();
35207         
35208         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35209         
35210         var _this = this;
35211                 
35212         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35213             _this.setPosition(_this.gMapContext.marker.position);
35214         });
35215         
35216         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35217             _this.fireEvent('mapClick', this, event);
35218             
35219         });
35220
35221         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35222             _this.fireEvent('mapRightClick', this, event);
35223             
35224         });
35225         
35226         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35227             _this.fireEvent('markerClick', this, event);
35228             
35229         });
35230
35231         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35232             _this.fireEvent('markerRightClick', this, event);
35233             
35234         });
35235         
35236         this.setPosition(this.gMapContext.location);
35237         
35238         this.fireEvent('initial', this, this.gMapContext.location);
35239     },
35240     
35241     initOverlayView: function()
35242     {
35243         var _this = this;
35244         
35245         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35246             
35247             draw: function()
35248             {
35249                 _this.fireEvent('OverlayViewDraw', _this);
35250             },
35251             
35252             onAdd: function()
35253             {
35254                 _this.fireEvent('OverlayViewOnAdd', _this);
35255             },
35256             
35257             onRemove: function()
35258             {
35259                 _this.fireEvent('OverlayViewOnRemove', _this);
35260             },
35261             
35262             show: function(cpx)
35263             {
35264                 _this.fireEvent('OverlayViewShow', _this, cpx);
35265             },
35266             
35267             hide: function()
35268             {
35269                 _this.fireEvent('OverlayViewHide', _this);
35270             }
35271             
35272         });
35273     },
35274     
35275     fromLatLngToContainerPixel: function(event)
35276     {
35277         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35278     },
35279     
35280     isApplied: function() 
35281     {
35282         return this.getGmapContext() == false ? false : true;
35283     },
35284     
35285     getGmapContext: function() 
35286     {
35287         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35288     },
35289     
35290     GMapContext: function() 
35291     {
35292         var position = new google.maps.LatLng(this.latitude, this.longitude);
35293         
35294         var _map = new google.maps.Map(this.el.dom, {
35295             center: position,
35296             zoom: this.zoom,
35297             mapTypeId: this.mapTypeId,
35298             mapTypeControl: this.mapTypeControl,
35299             disableDoubleClickZoom: this.disableDoubleClickZoom,
35300             scrollwheel: this.scrollwheel,
35301             streetViewControl: this.streetViewControl,
35302             locationName: this.locationName,
35303             draggable: this.draggable,
35304             enableAutocomplete: this.enableAutocomplete,
35305             enableReverseGeocode: this.enableReverseGeocode
35306         });
35307         
35308         var _marker = new google.maps.Marker({
35309             position: position,
35310             map: _map,
35311             title: this.markerTitle,
35312             draggable: this.draggable
35313         });
35314         
35315         return {
35316             map: _map,
35317             marker: _marker,
35318             circle: null,
35319             location: position,
35320             radius: this.radius,
35321             locationName: this.locationName,
35322             addressComponents: {
35323                 formatted_address: null,
35324                 addressLine1: null,
35325                 addressLine2: null,
35326                 streetName: null,
35327                 streetNumber: null,
35328                 city: null,
35329                 district: null,
35330                 state: null,
35331                 stateOrProvince: null
35332             },
35333             settings: this,
35334             domContainer: this.el.dom,
35335             geodecoder: new google.maps.Geocoder()
35336         };
35337     },
35338     
35339     drawCircle: function(center, radius, options) 
35340     {
35341         if (this.gMapContext.circle != null) {
35342             this.gMapContext.circle.setMap(null);
35343         }
35344         if (radius > 0) {
35345             radius *= 1;
35346             options = Roo.apply({}, options, {
35347                 strokeColor: "#0000FF",
35348                 strokeOpacity: .35,
35349                 strokeWeight: 2,
35350                 fillColor: "#0000FF",
35351                 fillOpacity: .2
35352             });
35353             
35354             options.map = this.gMapContext.map;
35355             options.radius = radius;
35356             options.center = center;
35357             this.gMapContext.circle = new google.maps.Circle(options);
35358             return this.gMapContext.circle;
35359         }
35360         
35361         return null;
35362     },
35363     
35364     setPosition: function(location) 
35365     {
35366         this.gMapContext.location = location;
35367         this.gMapContext.marker.setPosition(location);
35368         this.gMapContext.map.panTo(location);
35369         this.drawCircle(location, this.gMapContext.radius, {});
35370         
35371         var _this = this;
35372         
35373         if (this.gMapContext.settings.enableReverseGeocode) {
35374             this.gMapContext.geodecoder.geocode({
35375                 latLng: this.gMapContext.location
35376             }, function(results, status) {
35377                 
35378                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35379                     _this.gMapContext.locationName = results[0].formatted_address;
35380                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35381                     
35382                     _this.fireEvent('positionchanged', this, location);
35383                 }
35384             });
35385             
35386             return;
35387         }
35388         
35389         this.fireEvent('positionchanged', this, location);
35390     },
35391     
35392     resize: function()
35393     {
35394         google.maps.event.trigger(this.gMapContext.map, "resize");
35395         
35396         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35397         
35398         this.fireEvent('resize', this);
35399     },
35400     
35401     setPositionByLatLng: function(latitude, longitude)
35402     {
35403         this.setPosition(new google.maps.LatLng(latitude, longitude));
35404     },
35405     
35406     getCurrentPosition: function() 
35407     {
35408         return {
35409             latitude: this.gMapContext.location.lat(),
35410             longitude: this.gMapContext.location.lng()
35411         };
35412     },
35413     
35414     getAddressName: function() 
35415     {
35416         return this.gMapContext.locationName;
35417     },
35418     
35419     getAddressComponents: function() 
35420     {
35421         return this.gMapContext.addressComponents;
35422     },
35423     
35424     address_component_from_google_geocode: function(address_components) 
35425     {
35426         var result = {};
35427         
35428         for (var i = 0; i < address_components.length; i++) {
35429             var component = address_components[i];
35430             if (component.types.indexOf("postal_code") >= 0) {
35431                 result.postalCode = component.short_name;
35432             } else if (component.types.indexOf("street_number") >= 0) {
35433                 result.streetNumber = component.short_name;
35434             } else if (component.types.indexOf("route") >= 0) {
35435                 result.streetName = component.short_name;
35436             } else if (component.types.indexOf("neighborhood") >= 0) {
35437                 result.city = component.short_name;
35438             } else if (component.types.indexOf("locality") >= 0) {
35439                 result.city = component.short_name;
35440             } else if (component.types.indexOf("sublocality") >= 0) {
35441                 result.district = component.short_name;
35442             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35443                 result.stateOrProvince = component.short_name;
35444             } else if (component.types.indexOf("country") >= 0) {
35445                 result.country = component.short_name;
35446             }
35447         }
35448         
35449         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35450         result.addressLine2 = "";
35451         return result;
35452     },
35453     
35454     setZoomLevel: function(zoom)
35455     {
35456         this.gMapContext.map.setZoom(zoom);
35457     },
35458     
35459     show: function()
35460     {
35461         if(!this.el){
35462             return;
35463         }
35464         
35465         this.el.show();
35466         
35467         this.resize();
35468         
35469         this.fireEvent('show', this);
35470     },
35471     
35472     hide: function()
35473     {
35474         if(!this.el){
35475             return;
35476         }
35477         
35478         this.el.hide();
35479         
35480         this.fireEvent('hide', this);
35481     }
35482     
35483 });
35484
35485 Roo.apply(Roo.bootstrap.LocationPicker, {
35486     
35487     OverlayView : function(map, options)
35488     {
35489         options = options || {};
35490         
35491         this.setMap(map);
35492     }
35493     
35494     
35495 });/**
35496  * @class Roo.bootstrap.Alert
35497  * @extends Roo.bootstrap.Component
35498  * Bootstrap Alert class - shows an alert area box
35499  * eg
35500  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35501   Enter a valid email address
35502 </div>
35503  * @licence LGPL
35504  * @cfg {String} title The title of alert
35505  * @cfg {String} html The content of alert
35506  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35507  * @cfg {String} fa font-awesomeicon
35508  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35509  * @cfg {Boolean} close true to show a x closer
35510  * 
35511  * 
35512  * @constructor
35513  * Create a new alert
35514  * @param {Object} config The config object
35515  */
35516
35517
35518 Roo.bootstrap.Alert = function(config){
35519     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35520     
35521 };
35522
35523 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35524     
35525     title: '',
35526     html: '',
35527     weight: false,
35528     fa: false,
35529     faicon: false, // BC
35530     close : false,
35531     
35532     
35533     getAutoCreate : function()
35534     {
35535         
35536         var cfg = {
35537             tag : 'div',
35538             cls : 'alert',
35539             cn : [
35540                 {
35541                     tag: 'button',
35542                     type :  "button",
35543                     cls: "close",
35544                     html : '×',
35545                     style : this.close ? '' : 'display:none'
35546                 },
35547                 {
35548                     tag : 'i',
35549                     cls : 'roo-alert-icon'
35550                     
35551                 },
35552                 {
35553                     tag : 'b',
35554                     cls : 'roo-alert-title',
35555                     html : this.title
35556                 },
35557                 {
35558                     tag : 'span',
35559                     cls : 'roo-alert-text',
35560                     html : this.html
35561                 }
35562             ]
35563         };
35564         
35565         if(this.faicon){
35566             cfg.cn[0].cls += ' fa ' + this.faicon;
35567         }
35568         if(this.fa){
35569             cfg.cn[0].cls += ' fa ' + this.fa;
35570         }
35571         
35572         if(this.weight){
35573             cfg.cls += ' alert-' + this.weight;
35574         }
35575         
35576         return cfg;
35577     },
35578     
35579     initEvents: function() 
35580     {
35581         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35582         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35583         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35584         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35585         if (this.seconds > 0) {
35586             this.hide.defer(this.seconds, this);
35587         }
35588     },
35589     /**
35590      * Set the Title Message HTML
35591      * @param {String} html
35592      */
35593     setTitle : function(str)
35594     {
35595         this.titleEl.dom.innerHTML = str;
35596     },
35597      
35598      /**
35599      * Set the Body Message HTML
35600      * @param {String} html
35601      */
35602     setHtml : function(str)
35603     {
35604         this.htmlEl.dom.innerHTML = str;
35605     },
35606     /**
35607      * Set the Weight of the alert
35608      * @param {String} (success|info|warning|danger) weight
35609      */
35610     
35611     setWeight : function(weight)
35612     {
35613         if(this.weight){
35614             this.el.removeClass('alert-' + this.weight);
35615         }
35616         
35617         this.weight = weight;
35618         
35619         this.el.addClass('alert-' + this.weight);
35620     },
35621       /**
35622      * Set the Icon of the alert
35623      * @param {String} see fontawsome names (name without the 'fa-' bit)
35624      */
35625     setIcon : function(icon)
35626     {
35627         if(this.faicon){
35628             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35629         }
35630         
35631         this.faicon = icon;
35632         
35633         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35634     },
35635     /**
35636      * Hide the Alert
35637      */
35638     hide: function() 
35639     {
35640         this.el.hide();   
35641     },
35642     /**
35643      * Show the Alert
35644      */
35645     show: function() 
35646     {  
35647         this.el.show();   
35648     }
35649     
35650 });
35651
35652  
35653 /*
35654 * Licence: LGPL
35655 */
35656
35657 /**
35658  * @class Roo.bootstrap.UploadCropbox
35659  * @extends Roo.bootstrap.Component
35660  * Bootstrap UploadCropbox class
35661  * @cfg {String} emptyText show when image has been loaded
35662  * @cfg {String} rotateNotify show when image too small to rotate
35663  * @cfg {Number} errorTimeout default 3000
35664  * @cfg {Number} minWidth default 300
35665  * @cfg {Number} minHeight default 300
35666  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35667  * @cfg {Boolean} isDocument (true|false) default false
35668  * @cfg {String} url action url
35669  * @cfg {String} paramName default 'imageUpload'
35670  * @cfg {String} method default POST
35671  * @cfg {Boolean} loadMask (true|false) default true
35672  * @cfg {Boolean} loadingText default 'Loading...'
35673  * 
35674  * @constructor
35675  * Create a new UploadCropbox
35676  * @param {Object} config The config object
35677  */
35678
35679 Roo.bootstrap.UploadCropbox = function(config){
35680     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35681     
35682     this.addEvents({
35683         /**
35684          * @event beforeselectfile
35685          * Fire before select file
35686          * @param {Roo.bootstrap.UploadCropbox} this
35687          */
35688         "beforeselectfile" : true,
35689         /**
35690          * @event initial
35691          * Fire after initEvent
35692          * @param {Roo.bootstrap.UploadCropbox} this
35693          */
35694         "initial" : true,
35695         /**
35696          * @event crop
35697          * Fire after initEvent
35698          * @param {Roo.bootstrap.UploadCropbox} this
35699          * @param {String} data
35700          */
35701         "crop" : true,
35702         /**
35703          * @event prepare
35704          * Fire when preparing the file data
35705          * @param {Roo.bootstrap.UploadCropbox} this
35706          * @param {Object} file
35707          */
35708         "prepare" : true,
35709         /**
35710          * @event exception
35711          * Fire when get exception
35712          * @param {Roo.bootstrap.UploadCropbox} this
35713          * @param {XMLHttpRequest} xhr
35714          */
35715         "exception" : true,
35716         /**
35717          * @event beforeloadcanvas
35718          * Fire before load the canvas
35719          * @param {Roo.bootstrap.UploadCropbox} this
35720          * @param {String} src
35721          */
35722         "beforeloadcanvas" : true,
35723         /**
35724          * @event trash
35725          * Fire when trash image
35726          * @param {Roo.bootstrap.UploadCropbox} this
35727          */
35728         "trash" : true,
35729         /**
35730          * @event download
35731          * Fire when download the image
35732          * @param {Roo.bootstrap.UploadCropbox} this
35733          */
35734         "download" : true,
35735         /**
35736          * @event footerbuttonclick
35737          * Fire when footerbuttonclick
35738          * @param {Roo.bootstrap.UploadCropbox} this
35739          * @param {String} type
35740          */
35741         "footerbuttonclick" : true,
35742         /**
35743          * @event resize
35744          * Fire when resize
35745          * @param {Roo.bootstrap.UploadCropbox} this
35746          */
35747         "resize" : true,
35748         /**
35749          * @event rotate
35750          * Fire when rotate the image
35751          * @param {Roo.bootstrap.UploadCropbox} this
35752          * @param {String} pos
35753          */
35754         "rotate" : true,
35755         /**
35756          * @event inspect
35757          * Fire when inspect the file
35758          * @param {Roo.bootstrap.UploadCropbox} this
35759          * @param {Object} file
35760          */
35761         "inspect" : true,
35762         /**
35763          * @event upload
35764          * Fire when xhr upload the file
35765          * @param {Roo.bootstrap.UploadCropbox} this
35766          * @param {Object} data
35767          */
35768         "upload" : true,
35769         /**
35770          * @event arrange
35771          * Fire when arrange the file data
35772          * @param {Roo.bootstrap.UploadCropbox} this
35773          * @param {Object} formData
35774          */
35775         "arrange" : true
35776     });
35777     
35778     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35779 };
35780
35781 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35782     
35783     emptyText : 'Click to upload image',
35784     rotateNotify : 'Image is too small to rotate',
35785     errorTimeout : 3000,
35786     scale : 0,
35787     baseScale : 1,
35788     rotate : 0,
35789     dragable : false,
35790     pinching : false,
35791     mouseX : 0,
35792     mouseY : 0,
35793     cropData : false,
35794     minWidth : 300,
35795     minHeight : 300,
35796     file : false,
35797     exif : {},
35798     baseRotate : 1,
35799     cropType : 'image/jpeg',
35800     buttons : false,
35801     canvasLoaded : false,
35802     isDocument : false,
35803     method : 'POST',
35804     paramName : 'imageUpload',
35805     loadMask : true,
35806     loadingText : 'Loading...',
35807     maskEl : false,
35808     
35809     getAutoCreate : function()
35810     {
35811         var cfg = {
35812             tag : 'div',
35813             cls : 'roo-upload-cropbox',
35814             cn : [
35815                 {
35816                     tag : 'input',
35817                     cls : 'roo-upload-cropbox-selector',
35818                     type : 'file'
35819                 },
35820                 {
35821                     tag : 'div',
35822                     cls : 'roo-upload-cropbox-body',
35823                     style : 'cursor:pointer',
35824                     cn : [
35825                         {
35826                             tag : 'div',
35827                             cls : 'roo-upload-cropbox-preview'
35828                         },
35829                         {
35830                             tag : 'div',
35831                             cls : 'roo-upload-cropbox-thumb'
35832                         },
35833                         {
35834                             tag : 'div',
35835                             cls : 'roo-upload-cropbox-empty-notify',
35836                             html : this.emptyText
35837                         },
35838                         {
35839                             tag : 'div',
35840                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35841                             html : this.rotateNotify
35842                         }
35843                     ]
35844                 },
35845                 {
35846                     tag : 'div',
35847                     cls : 'roo-upload-cropbox-footer',
35848                     cn : {
35849                         tag : 'div',
35850                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35851                         cn : []
35852                     }
35853                 }
35854             ]
35855         };
35856         
35857         return cfg;
35858     },
35859     
35860     onRender : function(ct, position)
35861     {
35862         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35863         
35864         if (this.buttons.length) {
35865             
35866             Roo.each(this.buttons, function(bb) {
35867                 
35868                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35869                 
35870                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35871                 
35872             }, this);
35873         }
35874         
35875         if(this.loadMask){
35876             this.maskEl = this.el;
35877         }
35878     },
35879     
35880     initEvents : function()
35881     {
35882         this.urlAPI = (window.createObjectURL && window) || 
35883                                 (window.URL && URL.revokeObjectURL && URL) || 
35884                                 (window.webkitURL && webkitURL);
35885                         
35886         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35887         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35888         
35889         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35890         this.selectorEl.hide();
35891         
35892         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35893         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35894         
35895         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35896         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35897         this.thumbEl.hide();
35898         
35899         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35900         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35901         
35902         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35903         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35904         this.errorEl.hide();
35905         
35906         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35907         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35908         this.footerEl.hide();
35909         
35910         this.setThumbBoxSize();
35911         
35912         this.bind();
35913         
35914         this.resize();
35915         
35916         this.fireEvent('initial', this);
35917     },
35918
35919     bind : function()
35920     {
35921         var _this = this;
35922         
35923         window.addEventListener("resize", function() { _this.resize(); } );
35924         
35925         this.bodyEl.on('click', this.beforeSelectFile, this);
35926         
35927         if(Roo.isTouch){
35928             this.bodyEl.on('touchstart', this.onTouchStart, this);
35929             this.bodyEl.on('touchmove', this.onTouchMove, this);
35930             this.bodyEl.on('touchend', this.onTouchEnd, this);
35931         }
35932         
35933         if(!Roo.isTouch){
35934             this.bodyEl.on('mousedown', this.onMouseDown, this);
35935             this.bodyEl.on('mousemove', this.onMouseMove, this);
35936             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35937             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35938             Roo.get(document).on('mouseup', this.onMouseUp, this);
35939         }
35940         
35941         this.selectorEl.on('change', this.onFileSelected, this);
35942     },
35943     
35944     reset : function()
35945     {    
35946         this.scale = 0;
35947         this.baseScale = 1;
35948         this.rotate = 0;
35949         this.baseRotate = 1;
35950         this.dragable = false;
35951         this.pinching = false;
35952         this.mouseX = 0;
35953         this.mouseY = 0;
35954         this.cropData = false;
35955         this.notifyEl.dom.innerHTML = this.emptyText;
35956         
35957         this.selectorEl.dom.value = '';
35958         
35959     },
35960     
35961     resize : function()
35962     {
35963         if(this.fireEvent('resize', this) != false){
35964             this.setThumbBoxPosition();
35965             this.setCanvasPosition();
35966         }
35967     },
35968     
35969     onFooterButtonClick : function(e, el, o, type)
35970     {
35971         switch (type) {
35972             case 'rotate-left' :
35973                 this.onRotateLeft(e);
35974                 break;
35975             case 'rotate-right' :
35976                 this.onRotateRight(e);
35977                 break;
35978             case 'picture' :
35979                 this.beforeSelectFile(e);
35980                 break;
35981             case 'trash' :
35982                 this.trash(e);
35983                 break;
35984             case 'crop' :
35985                 this.crop(e);
35986                 break;
35987             case 'download' :
35988                 this.download(e);
35989                 break;
35990             default :
35991                 break;
35992         }
35993         
35994         this.fireEvent('footerbuttonclick', this, type);
35995     },
35996     
35997     beforeSelectFile : function(e)
35998     {
35999         e.preventDefault();
36000         
36001         if(this.fireEvent('beforeselectfile', this) != false){
36002             this.selectorEl.dom.click();
36003         }
36004     },
36005     
36006     onFileSelected : function(e)
36007     {
36008         e.preventDefault();
36009         
36010         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36011             return;
36012         }
36013         
36014         var file = this.selectorEl.dom.files[0];
36015         
36016         if(this.fireEvent('inspect', this, file) != false){
36017             this.prepare(file);
36018         }
36019         
36020     },
36021     
36022     trash : function(e)
36023     {
36024         this.fireEvent('trash', this);
36025     },
36026     
36027     download : function(e)
36028     {
36029         this.fireEvent('download', this);
36030     },
36031     
36032     loadCanvas : function(src)
36033     {   
36034         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36035             
36036             this.reset();
36037             
36038             this.imageEl = document.createElement('img');
36039             
36040             var _this = this;
36041             
36042             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36043             
36044             this.imageEl.src = src;
36045         }
36046     },
36047     
36048     onLoadCanvas : function()
36049     {   
36050         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36051         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36052         
36053         this.bodyEl.un('click', this.beforeSelectFile, this);
36054         
36055         this.notifyEl.hide();
36056         this.thumbEl.show();
36057         this.footerEl.show();
36058         
36059         this.baseRotateLevel();
36060         
36061         if(this.isDocument){
36062             this.setThumbBoxSize();
36063         }
36064         
36065         this.setThumbBoxPosition();
36066         
36067         this.baseScaleLevel();
36068         
36069         this.draw();
36070         
36071         this.resize();
36072         
36073         this.canvasLoaded = true;
36074         
36075         if(this.loadMask){
36076             this.maskEl.unmask();
36077         }
36078         
36079     },
36080     
36081     setCanvasPosition : function()
36082     {   
36083         if(!this.canvasEl){
36084             return;
36085         }
36086         
36087         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36088         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36089         
36090         this.previewEl.setLeft(pw);
36091         this.previewEl.setTop(ph);
36092         
36093     },
36094     
36095     onMouseDown : function(e)
36096     {   
36097         e.stopEvent();
36098         
36099         this.dragable = true;
36100         this.pinching = false;
36101         
36102         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36103             this.dragable = false;
36104             return;
36105         }
36106         
36107         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36108         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36109         
36110     },
36111     
36112     onMouseMove : function(e)
36113     {   
36114         e.stopEvent();
36115         
36116         if(!this.canvasLoaded){
36117             return;
36118         }
36119         
36120         if (!this.dragable){
36121             return;
36122         }
36123         
36124         var minX = Math.ceil(this.thumbEl.getLeft(true));
36125         var minY = Math.ceil(this.thumbEl.getTop(true));
36126         
36127         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36128         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36129         
36130         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36131         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36132         
36133         x = x - this.mouseX;
36134         y = y - this.mouseY;
36135         
36136         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36137         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36138         
36139         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36140         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36141         
36142         this.previewEl.setLeft(bgX);
36143         this.previewEl.setTop(bgY);
36144         
36145         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36146         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36147     },
36148     
36149     onMouseUp : function(e)
36150     {   
36151         e.stopEvent();
36152         
36153         this.dragable = false;
36154     },
36155     
36156     onMouseWheel : function(e)
36157     {   
36158         e.stopEvent();
36159         
36160         this.startScale = this.scale;
36161         
36162         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36163         
36164         if(!this.zoomable()){
36165             this.scale = this.startScale;
36166             return;
36167         }
36168         
36169         this.draw();
36170         
36171         return;
36172     },
36173     
36174     zoomable : function()
36175     {
36176         var minScale = this.thumbEl.getWidth() / this.minWidth;
36177         
36178         if(this.minWidth < this.minHeight){
36179             minScale = this.thumbEl.getHeight() / this.minHeight;
36180         }
36181         
36182         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36183         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36184         
36185         if(
36186                 this.isDocument &&
36187                 (this.rotate == 0 || this.rotate == 180) && 
36188                 (
36189                     width > this.imageEl.OriginWidth || 
36190                     height > this.imageEl.OriginHeight ||
36191                     (width < this.minWidth && height < this.minHeight)
36192                 )
36193         ){
36194             return false;
36195         }
36196         
36197         if(
36198                 this.isDocument &&
36199                 (this.rotate == 90 || this.rotate == 270) && 
36200                 (
36201                     width > this.imageEl.OriginWidth || 
36202                     height > this.imageEl.OriginHeight ||
36203                     (width < this.minHeight && height < this.minWidth)
36204                 )
36205         ){
36206             return false;
36207         }
36208         
36209         if(
36210                 !this.isDocument &&
36211                 (this.rotate == 0 || this.rotate == 180) && 
36212                 (
36213                     width < this.minWidth || 
36214                     width > this.imageEl.OriginWidth || 
36215                     height < this.minHeight || 
36216                     height > this.imageEl.OriginHeight
36217                 )
36218         ){
36219             return false;
36220         }
36221         
36222         if(
36223                 !this.isDocument &&
36224                 (this.rotate == 90 || this.rotate == 270) && 
36225                 (
36226                     width < this.minHeight || 
36227                     width > this.imageEl.OriginWidth || 
36228                     height < this.minWidth || 
36229                     height > this.imageEl.OriginHeight
36230                 )
36231         ){
36232             return false;
36233         }
36234         
36235         return true;
36236         
36237     },
36238     
36239     onRotateLeft : function(e)
36240     {   
36241         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36242             
36243             var minScale = this.thumbEl.getWidth() / this.minWidth;
36244             
36245             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36246             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36247             
36248             this.startScale = this.scale;
36249             
36250             while (this.getScaleLevel() < minScale){
36251             
36252                 this.scale = this.scale + 1;
36253                 
36254                 if(!this.zoomable()){
36255                     break;
36256                 }
36257                 
36258                 if(
36259                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36260                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36261                 ){
36262                     continue;
36263                 }
36264                 
36265                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36266
36267                 this.draw();
36268                 
36269                 return;
36270             }
36271             
36272             this.scale = this.startScale;
36273             
36274             this.onRotateFail();
36275             
36276             return false;
36277         }
36278         
36279         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36280
36281         if(this.isDocument){
36282             this.setThumbBoxSize();
36283             this.setThumbBoxPosition();
36284             this.setCanvasPosition();
36285         }
36286         
36287         this.draw();
36288         
36289         this.fireEvent('rotate', this, 'left');
36290         
36291     },
36292     
36293     onRotateRight : function(e)
36294     {
36295         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36296             
36297             var minScale = this.thumbEl.getWidth() / this.minWidth;
36298         
36299             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36300             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36301             
36302             this.startScale = this.scale;
36303             
36304             while (this.getScaleLevel() < minScale){
36305             
36306                 this.scale = this.scale + 1;
36307                 
36308                 if(!this.zoomable()){
36309                     break;
36310                 }
36311                 
36312                 if(
36313                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36314                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36315                 ){
36316                     continue;
36317                 }
36318                 
36319                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36320
36321                 this.draw();
36322                 
36323                 return;
36324             }
36325             
36326             this.scale = this.startScale;
36327             
36328             this.onRotateFail();
36329             
36330             return false;
36331         }
36332         
36333         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36334
36335         if(this.isDocument){
36336             this.setThumbBoxSize();
36337             this.setThumbBoxPosition();
36338             this.setCanvasPosition();
36339         }
36340         
36341         this.draw();
36342         
36343         this.fireEvent('rotate', this, 'right');
36344     },
36345     
36346     onRotateFail : function()
36347     {
36348         this.errorEl.show(true);
36349         
36350         var _this = this;
36351         
36352         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36353     },
36354     
36355     draw : function()
36356     {
36357         this.previewEl.dom.innerHTML = '';
36358         
36359         var canvasEl = document.createElement("canvas");
36360         
36361         var contextEl = canvasEl.getContext("2d");
36362         
36363         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36364         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36365         var center = this.imageEl.OriginWidth / 2;
36366         
36367         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36368             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36369             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36370             center = this.imageEl.OriginHeight / 2;
36371         }
36372         
36373         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36374         
36375         contextEl.translate(center, center);
36376         contextEl.rotate(this.rotate * Math.PI / 180);
36377
36378         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36379         
36380         this.canvasEl = document.createElement("canvas");
36381         
36382         this.contextEl = this.canvasEl.getContext("2d");
36383         
36384         switch (this.rotate) {
36385             case 0 :
36386                 
36387                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36388                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36389                 
36390                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36391                 
36392                 break;
36393             case 90 : 
36394                 
36395                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36396                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36397                 
36398                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36399                     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);
36400                     break;
36401                 }
36402                 
36403                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36404                 
36405                 break;
36406             case 180 :
36407                 
36408                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36409                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36410                 
36411                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36412                     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);
36413                     break;
36414                 }
36415                 
36416                 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);
36417                 
36418                 break;
36419             case 270 :
36420                 
36421                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36422                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36423         
36424                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36425                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36426                     break;
36427                 }
36428                 
36429                 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);
36430                 
36431                 break;
36432             default : 
36433                 break;
36434         }
36435         
36436         this.previewEl.appendChild(this.canvasEl);
36437         
36438         this.setCanvasPosition();
36439     },
36440     
36441     crop : function()
36442     {
36443         if(!this.canvasLoaded){
36444             return;
36445         }
36446         
36447         var imageCanvas = document.createElement("canvas");
36448         
36449         var imageContext = imageCanvas.getContext("2d");
36450         
36451         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36452         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36453         
36454         var center = imageCanvas.width / 2;
36455         
36456         imageContext.translate(center, center);
36457         
36458         imageContext.rotate(this.rotate * Math.PI / 180);
36459         
36460         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36461         
36462         var canvas = document.createElement("canvas");
36463         
36464         var context = canvas.getContext("2d");
36465                 
36466         canvas.width = this.minWidth;
36467         canvas.height = this.minHeight;
36468
36469         switch (this.rotate) {
36470             case 0 :
36471                 
36472                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36473                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36474                 
36475                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36476                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36477                 
36478                 var targetWidth = this.minWidth - 2 * x;
36479                 var targetHeight = this.minHeight - 2 * y;
36480                 
36481                 var scale = 1;
36482                 
36483                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36484                     scale = targetWidth / width;
36485                 }
36486                 
36487                 if(x > 0 && y == 0){
36488                     scale = targetHeight / height;
36489                 }
36490                 
36491                 if(x > 0 && y > 0){
36492                     scale = targetWidth / width;
36493                     
36494                     if(width < height){
36495                         scale = targetHeight / height;
36496                     }
36497                 }
36498                 
36499                 context.scale(scale, scale);
36500                 
36501                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36502                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36503
36504                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36505                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36506
36507                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36508                 
36509                 break;
36510             case 90 : 
36511                 
36512                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36513                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36514                 
36515                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36516                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36517                 
36518                 var targetWidth = this.minWidth - 2 * x;
36519                 var targetHeight = this.minHeight - 2 * y;
36520                 
36521                 var scale = 1;
36522                 
36523                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36524                     scale = targetWidth / width;
36525                 }
36526                 
36527                 if(x > 0 && y == 0){
36528                     scale = targetHeight / height;
36529                 }
36530                 
36531                 if(x > 0 && y > 0){
36532                     scale = targetWidth / width;
36533                     
36534                     if(width < height){
36535                         scale = targetHeight / height;
36536                     }
36537                 }
36538                 
36539                 context.scale(scale, scale);
36540                 
36541                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36542                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36543
36544                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36545                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36546                 
36547                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36548                 
36549                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36550                 
36551                 break;
36552             case 180 :
36553                 
36554                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36555                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36556                 
36557                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36558                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36559                 
36560                 var targetWidth = this.minWidth - 2 * x;
36561                 var targetHeight = this.minHeight - 2 * y;
36562                 
36563                 var scale = 1;
36564                 
36565                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36566                     scale = targetWidth / width;
36567                 }
36568                 
36569                 if(x > 0 && y == 0){
36570                     scale = targetHeight / height;
36571                 }
36572                 
36573                 if(x > 0 && y > 0){
36574                     scale = targetWidth / width;
36575                     
36576                     if(width < height){
36577                         scale = targetHeight / height;
36578                     }
36579                 }
36580                 
36581                 context.scale(scale, scale);
36582                 
36583                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36584                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36585
36586                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36587                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36588
36589                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36590                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36591                 
36592                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36593                 
36594                 break;
36595             case 270 :
36596                 
36597                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36598                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36599                 
36600                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36601                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36602                 
36603                 var targetWidth = this.minWidth - 2 * x;
36604                 var targetHeight = this.minHeight - 2 * y;
36605                 
36606                 var scale = 1;
36607                 
36608                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36609                     scale = targetWidth / width;
36610                 }
36611                 
36612                 if(x > 0 && y == 0){
36613                     scale = targetHeight / height;
36614                 }
36615                 
36616                 if(x > 0 && y > 0){
36617                     scale = targetWidth / width;
36618                     
36619                     if(width < height){
36620                         scale = targetHeight / height;
36621                     }
36622                 }
36623                 
36624                 context.scale(scale, scale);
36625                 
36626                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36627                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36628
36629                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36630                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36631                 
36632                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36633                 
36634                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36635                 
36636                 break;
36637             default : 
36638                 break;
36639         }
36640         
36641         this.cropData = canvas.toDataURL(this.cropType);
36642         
36643         if(this.fireEvent('crop', this, this.cropData) !== false){
36644             this.process(this.file, this.cropData);
36645         }
36646         
36647         return;
36648         
36649     },
36650     
36651     setThumbBoxSize : function()
36652     {
36653         var width, height;
36654         
36655         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36656             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36657             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36658             
36659             this.minWidth = width;
36660             this.minHeight = height;
36661             
36662             if(this.rotate == 90 || this.rotate == 270){
36663                 this.minWidth = height;
36664                 this.minHeight = width;
36665             }
36666         }
36667         
36668         height = 300;
36669         width = Math.ceil(this.minWidth * height / this.minHeight);
36670         
36671         if(this.minWidth > this.minHeight){
36672             width = 300;
36673             height = Math.ceil(this.minHeight * width / this.minWidth);
36674         }
36675         
36676         this.thumbEl.setStyle({
36677             width : width + 'px',
36678             height : height + 'px'
36679         });
36680
36681         return;
36682             
36683     },
36684     
36685     setThumbBoxPosition : function()
36686     {
36687         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36688         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36689         
36690         this.thumbEl.setLeft(x);
36691         this.thumbEl.setTop(y);
36692         
36693     },
36694     
36695     baseRotateLevel : function()
36696     {
36697         this.baseRotate = 1;
36698         
36699         if(
36700                 typeof(this.exif) != 'undefined' &&
36701                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36702                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36703         ){
36704             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36705         }
36706         
36707         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36708         
36709     },
36710     
36711     baseScaleLevel : function()
36712     {
36713         var width, height;
36714         
36715         if(this.isDocument){
36716             
36717             if(this.baseRotate == 6 || this.baseRotate == 8){
36718             
36719                 height = this.thumbEl.getHeight();
36720                 this.baseScale = height / this.imageEl.OriginWidth;
36721
36722                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36723                     width = this.thumbEl.getWidth();
36724                     this.baseScale = width / this.imageEl.OriginHeight;
36725                 }
36726
36727                 return;
36728             }
36729
36730             height = this.thumbEl.getHeight();
36731             this.baseScale = height / this.imageEl.OriginHeight;
36732
36733             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36734                 width = this.thumbEl.getWidth();
36735                 this.baseScale = width / this.imageEl.OriginWidth;
36736             }
36737
36738             return;
36739         }
36740         
36741         if(this.baseRotate == 6 || this.baseRotate == 8){
36742             
36743             width = this.thumbEl.getHeight();
36744             this.baseScale = width / this.imageEl.OriginHeight;
36745             
36746             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36747                 height = this.thumbEl.getWidth();
36748                 this.baseScale = height / this.imageEl.OriginHeight;
36749             }
36750             
36751             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36752                 height = this.thumbEl.getWidth();
36753                 this.baseScale = height / this.imageEl.OriginHeight;
36754                 
36755                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36756                     width = this.thumbEl.getHeight();
36757                     this.baseScale = width / this.imageEl.OriginWidth;
36758                 }
36759             }
36760             
36761             return;
36762         }
36763         
36764         width = this.thumbEl.getWidth();
36765         this.baseScale = width / this.imageEl.OriginWidth;
36766         
36767         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36768             height = this.thumbEl.getHeight();
36769             this.baseScale = height / this.imageEl.OriginHeight;
36770         }
36771         
36772         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36773             
36774             height = this.thumbEl.getHeight();
36775             this.baseScale = height / this.imageEl.OriginHeight;
36776             
36777             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36778                 width = this.thumbEl.getWidth();
36779                 this.baseScale = width / this.imageEl.OriginWidth;
36780             }
36781             
36782         }
36783         
36784         return;
36785     },
36786     
36787     getScaleLevel : function()
36788     {
36789         return this.baseScale * Math.pow(1.1, this.scale);
36790     },
36791     
36792     onTouchStart : function(e)
36793     {
36794         if(!this.canvasLoaded){
36795             this.beforeSelectFile(e);
36796             return;
36797         }
36798         
36799         var touches = e.browserEvent.touches;
36800         
36801         if(!touches){
36802             return;
36803         }
36804         
36805         if(touches.length == 1){
36806             this.onMouseDown(e);
36807             return;
36808         }
36809         
36810         if(touches.length != 2){
36811             return;
36812         }
36813         
36814         var coords = [];
36815         
36816         for(var i = 0, finger; finger = touches[i]; i++){
36817             coords.push(finger.pageX, finger.pageY);
36818         }
36819         
36820         var x = Math.pow(coords[0] - coords[2], 2);
36821         var y = Math.pow(coords[1] - coords[3], 2);
36822         
36823         this.startDistance = Math.sqrt(x + y);
36824         
36825         this.startScale = this.scale;
36826         
36827         this.pinching = true;
36828         this.dragable = false;
36829         
36830     },
36831     
36832     onTouchMove : function(e)
36833     {
36834         if(!this.pinching && !this.dragable){
36835             return;
36836         }
36837         
36838         var touches = e.browserEvent.touches;
36839         
36840         if(!touches){
36841             return;
36842         }
36843         
36844         if(this.dragable){
36845             this.onMouseMove(e);
36846             return;
36847         }
36848         
36849         var coords = [];
36850         
36851         for(var i = 0, finger; finger = touches[i]; i++){
36852             coords.push(finger.pageX, finger.pageY);
36853         }
36854         
36855         var x = Math.pow(coords[0] - coords[2], 2);
36856         var y = Math.pow(coords[1] - coords[3], 2);
36857         
36858         this.endDistance = Math.sqrt(x + y);
36859         
36860         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36861         
36862         if(!this.zoomable()){
36863             this.scale = this.startScale;
36864             return;
36865         }
36866         
36867         this.draw();
36868         
36869     },
36870     
36871     onTouchEnd : function(e)
36872     {
36873         this.pinching = false;
36874         this.dragable = false;
36875         
36876     },
36877     
36878     process : function(file, crop)
36879     {
36880         if(this.loadMask){
36881             this.maskEl.mask(this.loadingText);
36882         }
36883         
36884         this.xhr = new XMLHttpRequest();
36885         
36886         file.xhr = this.xhr;
36887
36888         this.xhr.open(this.method, this.url, true);
36889         
36890         var headers = {
36891             "Accept": "application/json",
36892             "Cache-Control": "no-cache",
36893             "X-Requested-With": "XMLHttpRequest"
36894         };
36895         
36896         for (var headerName in headers) {
36897             var headerValue = headers[headerName];
36898             if (headerValue) {
36899                 this.xhr.setRequestHeader(headerName, headerValue);
36900             }
36901         }
36902         
36903         var _this = this;
36904         
36905         this.xhr.onload = function()
36906         {
36907             _this.xhrOnLoad(_this.xhr);
36908         }
36909         
36910         this.xhr.onerror = function()
36911         {
36912             _this.xhrOnError(_this.xhr);
36913         }
36914         
36915         var formData = new FormData();
36916
36917         formData.append('returnHTML', 'NO');
36918         
36919         if(crop){
36920             formData.append('crop', crop);
36921         }
36922         
36923         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36924             formData.append(this.paramName, file, file.name);
36925         }
36926         
36927         if(typeof(file.filename) != 'undefined'){
36928             formData.append('filename', file.filename);
36929         }
36930         
36931         if(typeof(file.mimetype) != 'undefined'){
36932             formData.append('mimetype', file.mimetype);
36933         }
36934         
36935         if(this.fireEvent('arrange', this, formData) != false){
36936             this.xhr.send(formData);
36937         };
36938     },
36939     
36940     xhrOnLoad : function(xhr)
36941     {
36942         if(this.loadMask){
36943             this.maskEl.unmask();
36944         }
36945         
36946         if (xhr.readyState !== 4) {
36947             this.fireEvent('exception', this, xhr);
36948             return;
36949         }
36950
36951         var response = Roo.decode(xhr.responseText);
36952         
36953         if(!response.success){
36954             this.fireEvent('exception', this, xhr);
36955             return;
36956         }
36957         
36958         var response = Roo.decode(xhr.responseText);
36959         
36960         this.fireEvent('upload', this, response);
36961         
36962     },
36963     
36964     xhrOnError : function()
36965     {
36966         if(this.loadMask){
36967             this.maskEl.unmask();
36968         }
36969         
36970         Roo.log('xhr on error');
36971         
36972         var response = Roo.decode(xhr.responseText);
36973           
36974         Roo.log(response);
36975         
36976     },
36977     
36978     prepare : function(file)
36979     {   
36980         if(this.loadMask){
36981             this.maskEl.mask(this.loadingText);
36982         }
36983         
36984         this.file = false;
36985         this.exif = {};
36986         
36987         if(typeof(file) === 'string'){
36988             this.loadCanvas(file);
36989             return;
36990         }
36991         
36992         if(!file || !this.urlAPI){
36993             return;
36994         }
36995         
36996         this.file = file;
36997         this.cropType = file.type;
36998         
36999         var _this = this;
37000         
37001         if(this.fireEvent('prepare', this, this.file) != false){
37002             
37003             var reader = new FileReader();
37004             
37005             reader.onload = function (e) {
37006                 if (e.target.error) {
37007                     Roo.log(e.target.error);
37008                     return;
37009                 }
37010                 
37011                 var buffer = e.target.result,
37012                     dataView = new DataView(buffer),
37013                     offset = 2,
37014                     maxOffset = dataView.byteLength - 4,
37015                     markerBytes,
37016                     markerLength;
37017                 
37018                 if (dataView.getUint16(0) === 0xffd8) {
37019                     while (offset < maxOffset) {
37020                         markerBytes = dataView.getUint16(offset);
37021                         
37022                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37023                             markerLength = dataView.getUint16(offset + 2) + 2;
37024                             if (offset + markerLength > dataView.byteLength) {
37025                                 Roo.log('Invalid meta data: Invalid segment size.');
37026                                 break;
37027                             }
37028                             
37029                             if(markerBytes == 0xffe1){
37030                                 _this.parseExifData(
37031                                     dataView,
37032                                     offset,
37033                                     markerLength
37034                                 );
37035                             }
37036                             
37037                             offset += markerLength;
37038                             
37039                             continue;
37040                         }
37041                         
37042                         break;
37043                     }
37044                     
37045                 }
37046                 
37047                 var url = _this.urlAPI.createObjectURL(_this.file);
37048                 
37049                 _this.loadCanvas(url);
37050                 
37051                 return;
37052             }
37053             
37054             reader.readAsArrayBuffer(this.file);
37055             
37056         }
37057         
37058     },
37059     
37060     parseExifData : function(dataView, offset, length)
37061     {
37062         var tiffOffset = offset + 10,
37063             littleEndian,
37064             dirOffset;
37065     
37066         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37067             // No Exif data, might be XMP data instead
37068             return;
37069         }
37070         
37071         // Check for the ASCII code for "Exif" (0x45786966):
37072         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37073             // No Exif data, might be XMP data instead
37074             return;
37075         }
37076         if (tiffOffset + 8 > dataView.byteLength) {
37077             Roo.log('Invalid Exif data: Invalid segment size.');
37078             return;
37079         }
37080         // Check for the two null bytes:
37081         if (dataView.getUint16(offset + 8) !== 0x0000) {
37082             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37083             return;
37084         }
37085         // Check the byte alignment:
37086         switch (dataView.getUint16(tiffOffset)) {
37087         case 0x4949:
37088             littleEndian = true;
37089             break;
37090         case 0x4D4D:
37091             littleEndian = false;
37092             break;
37093         default:
37094             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37095             return;
37096         }
37097         // Check for the TIFF tag marker (0x002A):
37098         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37099             Roo.log('Invalid Exif data: Missing TIFF marker.');
37100             return;
37101         }
37102         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37103         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37104         
37105         this.parseExifTags(
37106             dataView,
37107             tiffOffset,
37108             tiffOffset + dirOffset,
37109             littleEndian
37110         );
37111     },
37112     
37113     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37114     {
37115         var tagsNumber,
37116             dirEndOffset,
37117             i;
37118         if (dirOffset + 6 > dataView.byteLength) {
37119             Roo.log('Invalid Exif data: Invalid directory offset.');
37120             return;
37121         }
37122         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37123         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37124         if (dirEndOffset + 4 > dataView.byteLength) {
37125             Roo.log('Invalid Exif data: Invalid directory size.');
37126             return;
37127         }
37128         for (i = 0; i < tagsNumber; i += 1) {
37129             this.parseExifTag(
37130                 dataView,
37131                 tiffOffset,
37132                 dirOffset + 2 + 12 * i, // tag offset
37133                 littleEndian
37134             );
37135         }
37136         // Return the offset to the next directory:
37137         return dataView.getUint32(dirEndOffset, littleEndian);
37138     },
37139     
37140     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37141     {
37142         var tag = dataView.getUint16(offset, littleEndian);
37143         
37144         this.exif[tag] = this.getExifValue(
37145             dataView,
37146             tiffOffset,
37147             offset,
37148             dataView.getUint16(offset + 2, littleEndian), // tag type
37149             dataView.getUint32(offset + 4, littleEndian), // tag length
37150             littleEndian
37151         );
37152     },
37153     
37154     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37155     {
37156         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37157             tagSize,
37158             dataOffset,
37159             values,
37160             i,
37161             str,
37162             c;
37163     
37164         if (!tagType) {
37165             Roo.log('Invalid Exif data: Invalid tag type.');
37166             return;
37167         }
37168         
37169         tagSize = tagType.size * length;
37170         // Determine if the value is contained in the dataOffset bytes,
37171         // or if the value at the dataOffset is a pointer to the actual data:
37172         dataOffset = tagSize > 4 ?
37173                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37174         if (dataOffset + tagSize > dataView.byteLength) {
37175             Roo.log('Invalid Exif data: Invalid data offset.');
37176             return;
37177         }
37178         if (length === 1) {
37179             return tagType.getValue(dataView, dataOffset, littleEndian);
37180         }
37181         values = [];
37182         for (i = 0; i < length; i += 1) {
37183             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37184         }
37185         
37186         if (tagType.ascii) {
37187             str = '';
37188             // Concatenate the chars:
37189             for (i = 0; i < values.length; i += 1) {
37190                 c = values[i];
37191                 // Ignore the terminating NULL byte(s):
37192                 if (c === '\u0000') {
37193                     break;
37194                 }
37195                 str += c;
37196             }
37197             return str;
37198         }
37199         return values;
37200     }
37201     
37202 });
37203
37204 Roo.apply(Roo.bootstrap.UploadCropbox, {
37205     tags : {
37206         'Orientation': 0x0112
37207     },
37208     
37209     Orientation: {
37210             1: 0, //'top-left',
37211 //            2: 'top-right',
37212             3: 180, //'bottom-right',
37213 //            4: 'bottom-left',
37214 //            5: 'left-top',
37215             6: 90, //'right-top',
37216 //            7: 'right-bottom',
37217             8: 270 //'left-bottom'
37218     },
37219     
37220     exifTagTypes : {
37221         // byte, 8-bit unsigned int:
37222         1: {
37223             getValue: function (dataView, dataOffset) {
37224                 return dataView.getUint8(dataOffset);
37225             },
37226             size: 1
37227         },
37228         // ascii, 8-bit byte:
37229         2: {
37230             getValue: function (dataView, dataOffset) {
37231                 return String.fromCharCode(dataView.getUint8(dataOffset));
37232             },
37233             size: 1,
37234             ascii: true
37235         },
37236         // short, 16 bit int:
37237         3: {
37238             getValue: function (dataView, dataOffset, littleEndian) {
37239                 return dataView.getUint16(dataOffset, littleEndian);
37240             },
37241             size: 2
37242         },
37243         // long, 32 bit int:
37244         4: {
37245             getValue: function (dataView, dataOffset, littleEndian) {
37246                 return dataView.getUint32(dataOffset, littleEndian);
37247             },
37248             size: 4
37249         },
37250         // rational = two long values, first is numerator, second is denominator:
37251         5: {
37252             getValue: function (dataView, dataOffset, littleEndian) {
37253                 return dataView.getUint32(dataOffset, littleEndian) /
37254                     dataView.getUint32(dataOffset + 4, littleEndian);
37255             },
37256             size: 8
37257         },
37258         // slong, 32 bit signed int:
37259         9: {
37260             getValue: function (dataView, dataOffset, littleEndian) {
37261                 return dataView.getInt32(dataOffset, littleEndian);
37262             },
37263             size: 4
37264         },
37265         // srational, two slongs, first is numerator, second is denominator:
37266         10: {
37267             getValue: function (dataView, dataOffset, littleEndian) {
37268                 return dataView.getInt32(dataOffset, littleEndian) /
37269                     dataView.getInt32(dataOffset + 4, littleEndian);
37270             },
37271             size: 8
37272         }
37273     },
37274     
37275     footer : {
37276         STANDARD : [
37277             {
37278                 tag : 'div',
37279                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37280                 action : 'rotate-left',
37281                 cn : [
37282                     {
37283                         tag : 'button',
37284                         cls : 'btn btn-default',
37285                         html : '<i class="fa fa-undo"></i>'
37286                     }
37287                 ]
37288             },
37289             {
37290                 tag : 'div',
37291                 cls : 'btn-group roo-upload-cropbox-picture',
37292                 action : 'picture',
37293                 cn : [
37294                     {
37295                         tag : 'button',
37296                         cls : 'btn btn-default',
37297                         html : '<i class="fa fa-picture-o"></i>'
37298                     }
37299                 ]
37300             },
37301             {
37302                 tag : 'div',
37303                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37304                 action : 'rotate-right',
37305                 cn : [
37306                     {
37307                         tag : 'button',
37308                         cls : 'btn btn-default',
37309                         html : '<i class="fa fa-repeat"></i>'
37310                     }
37311                 ]
37312             }
37313         ],
37314         DOCUMENT : [
37315             {
37316                 tag : 'div',
37317                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37318                 action : 'rotate-left',
37319                 cn : [
37320                     {
37321                         tag : 'button',
37322                         cls : 'btn btn-default',
37323                         html : '<i class="fa fa-undo"></i>'
37324                     }
37325                 ]
37326             },
37327             {
37328                 tag : 'div',
37329                 cls : 'btn-group roo-upload-cropbox-download',
37330                 action : 'download',
37331                 cn : [
37332                     {
37333                         tag : 'button',
37334                         cls : 'btn btn-default',
37335                         html : '<i class="fa fa-download"></i>'
37336                     }
37337                 ]
37338             },
37339             {
37340                 tag : 'div',
37341                 cls : 'btn-group roo-upload-cropbox-crop',
37342                 action : 'crop',
37343                 cn : [
37344                     {
37345                         tag : 'button',
37346                         cls : 'btn btn-default',
37347                         html : '<i class="fa fa-crop"></i>'
37348                     }
37349                 ]
37350             },
37351             {
37352                 tag : 'div',
37353                 cls : 'btn-group roo-upload-cropbox-trash',
37354                 action : 'trash',
37355                 cn : [
37356                     {
37357                         tag : 'button',
37358                         cls : 'btn btn-default',
37359                         html : '<i class="fa fa-trash"></i>'
37360                     }
37361                 ]
37362             },
37363             {
37364                 tag : 'div',
37365                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37366                 action : 'rotate-right',
37367                 cn : [
37368                     {
37369                         tag : 'button',
37370                         cls : 'btn btn-default',
37371                         html : '<i class="fa fa-repeat"></i>'
37372                     }
37373                 ]
37374             }
37375         ],
37376         ROTATOR : [
37377             {
37378                 tag : 'div',
37379                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37380                 action : 'rotate-left',
37381                 cn : [
37382                     {
37383                         tag : 'button',
37384                         cls : 'btn btn-default',
37385                         html : '<i class="fa fa-undo"></i>'
37386                     }
37387                 ]
37388             },
37389             {
37390                 tag : 'div',
37391                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37392                 action : 'rotate-right',
37393                 cn : [
37394                     {
37395                         tag : 'button',
37396                         cls : 'btn btn-default',
37397                         html : '<i class="fa fa-repeat"></i>'
37398                     }
37399                 ]
37400             }
37401         ]
37402     }
37403 });
37404
37405 /*
37406 * Licence: LGPL
37407 */
37408
37409 /**
37410  * @class Roo.bootstrap.DocumentManager
37411  * @extends Roo.bootstrap.Component
37412  * Bootstrap DocumentManager class
37413  * @cfg {String} paramName default 'imageUpload'
37414  * @cfg {String} toolTipName default 'filename'
37415  * @cfg {String} method default POST
37416  * @cfg {String} url action url
37417  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37418  * @cfg {Boolean} multiple multiple upload default true
37419  * @cfg {Number} thumbSize default 300
37420  * @cfg {String} fieldLabel
37421  * @cfg {Number} labelWidth default 4
37422  * @cfg {String} labelAlign (left|top) default left
37423  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37424 * @cfg {Number} labellg set the width of label (1-12)
37425  * @cfg {Number} labelmd set the width of label (1-12)
37426  * @cfg {Number} labelsm set the width of label (1-12)
37427  * @cfg {Number} labelxs set the width of label (1-12)
37428  * 
37429  * @constructor
37430  * Create a new DocumentManager
37431  * @param {Object} config The config object
37432  */
37433
37434 Roo.bootstrap.DocumentManager = function(config){
37435     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37436     
37437     this.files = [];
37438     this.delegates = [];
37439     
37440     this.addEvents({
37441         /**
37442          * @event initial
37443          * Fire when initial the DocumentManager
37444          * @param {Roo.bootstrap.DocumentManager} this
37445          */
37446         "initial" : true,
37447         /**
37448          * @event inspect
37449          * inspect selected file
37450          * @param {Roo.bootstrap.DocumentManager} this
37451          * @param {File} file
37452          */
37453         "inspect" : true,
37454         /**
37455          * @event exception
37456          * Fire when xhr load exception
37457          * @param {Roo.bootstrap.DocumentManager} this
37458          * @param {XMLHttpRequest} xhr
37459          */
37460         "exception" : true,
37461         /**
37462          * @event afterupload
37463          * Fire when xhr load exception
37464          * @param {Roo.bootstrap.DocumentManager} this
37465          * @param {XMLHttpRequest} xhr
37466          */
37467         "afterupload" : true,
37468         /**
37469          * @event prepare
37470          * prepare the form data
37471          * @param {Roo.bootstrap.DocumentManager} this
37472          * @param {Object} formData
37473          */
37474         "prepare" : true,
37475         /**
37476          * @event remove
37477          * Fire when remove the file
37478          * @param {Roo.bootstrap.DocumentManager} this
37479          * @param {Object} file
37480          */
37481         "remove" : true,
37482         /**
37483          * @event refresh
37484          * Fire after refresh the file
37485          * @param {Roo.bootstrap.DocumentManager} this
37486          */
37487         "refresh" : true,
37488         /**
37489          * @event click
37490          * Fire after click the image
37491          * @param {Roo.bootstrap.DocumentManager} this
37492          * @param {Object} file
37493          */
37494         "click" : true,
37495         /**
37496          * @event edit
37497          * Fire when upload a image and editable set to true
37498          * @param {Roo.bootstrap.DocumentManager} this
37499          * @param {Object} file
37500          */
37501         "edit" : true,
37502         /**
37503          * @event beforeselectfile
37504          * Fire before select file
37505          * @param {Roo.bootstrap.DocumentManager} this
37506          */
37507         "beforeselectfile" : true,
37508         /**
37509          * @event process
37510          * Fire before process file
37511          * @param {Roo.bootstrap.DocumentManager} this
37512          * @param {Object} file
37513          */
37514         "process" : true,
37515         /**
37516          * @event previewrendered
37517          * Fire when preview rendered
37518          * @param {Roo.bootstrap.DocumentManager} this
37519          * @param {Object} file
37520          */
37521         "previewrendered" : true,
37522         /**
37523          */
37524         "previewResize" : true
37525         
37526     });
37527 };
37528
37529 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37530     
37531     boxes : 0,
37532     inputName : '',
37533     thumbSize : 300,
37534     multiple : true,
37535     files : false,
37536     method : 'POST',
37537     url : '',
37538     paramName : 'imageUpload',
37539     toolTipName : 'filename',
37540     fieldLabel : '',
37541     labelWidth : 4,
37542     labelAlign : 'left',
37543     editable : true,
37544     delegates : false,
37545     xhr : false, 
37546     
37547     labellg : 0,
37548     labelmd : 0,
37549     labelsm : 0,
37550     labelxs : 0,
37551     
37552     getAutoCreate : function()
37553     {   
37554         var managerWidget = {
37555             tag : 'div',
37556             cls : 'roo-document-manager',
37557             cn : [
37558                 {
37559                     tag : 'input',
37560                     cls : 'roo-document-manager-selector',
37561                     type : 'file'
37562                 },
37563                 {
37564                     tag : 'div',
37565                     cls : 'roo-document-manager-uploader',
37566                     cn : [
37567                         {
37568                             tag : 'div',
37569                             cls : 'roo-document-manager-upload-btn',
37570                             html : '<i class="fa fa-plus"></i>'
37571                         }
37572                     ]
37573                     
37574                 }
37575             ]
37576         };
37577         
37578         var content = [
37579             {
37580                 tag : 'div',
37581                 cls : 'column col-md-12',
37582                 cn : managerWidget
37583             }
37584         ];
37585         
37586         if(this.fieldLabel.length){
37587             
37588             content = [
37589                 {
37590                     tag : 'div',
37591                     cls : 'column col-md-12',
37592                     html : this.fieldLabel
37593                 },
37594                 {
37595                     tag : 'div',
37596                     cls : 'column col-md-12',
37597                     cn : managerWidget
37598                 }
37599             ];
37600
37601             if(this.labelAlign == 'left'){
37602                 content = [
37603                     {
37604                         tag : 'div',
37605                         cls : 'column',
37606                         html : this.fieldLabel
37607                     },
37608                     {
37609                         tag : 'div',
37610                         cls : 'column',
37611                         cn : managerWidget
37612                     }
37613                 ];
37614                 
37615                 if(this.labelWidth > 12){
37616                     content[0].style = "width: " + this.labelWidth + 'px';
37617                 }
37618
37619                 if(this.labelWidth < 13 && this.labelmd == 0){
37620                     this.labelmd = this.labelWidth;
37621                 }
37622
37623                 if(this.labellg > 0){
37624                     content[0].cls += ' col-lg-' + this.labellg;
37625                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37626                 }
37627
37628                 if(this.labelmd > 0){
37629                     content[0].cls += ' col-md-' + this.labelmd;
37630                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37631                 }
37632
37633                 if(this.labelsm > 0){
37634                     content[0].cls += ' col-sm-' + this.labelsm;
37635                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37636                 }
37637
37638                 if(this.labelxs > 0){
37639                     content[0].cls += ' col-xs-' + this.labelxs;
37640                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37641                 }
37642                 
37643             }
37644         }
37645         
37646         var cfg = {
37647             tag : 'div',
37648             cls : 'row clearfix',
37649             cn : content
37650         };
37651         
37652         return cfg;
37653         
37654     },
37655     
37656     initEvents : function()
37657     {
37658         this.managerEl = this.el.select('.roo-document-manager', true).first();
37659         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37660         
37661         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37662         this.selectorEl.hide();
37663         
37664         if(this.multiple){
37665             this.selectorEl.attr('multiple', 'multiple');
37666         }
37667         
37668         this.selectorEl.on('change', this.onFileSelected, this);
37669         
37670         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37671         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37672         
37673         this.uploader.on('click', this.onUploaderClick, this);
37674         
37675         this.renderProgressDialog();
37676         
37677         var _this = this;
37678         
37679         window.addEventListener("resize", function() { _this.refresh(); } );
37680         
37681         this.fireEvent('initial', this);
37682     },
37683     
37684     renderProgressDialog : function()
37685     {
37686         var _this = this;
37687         
37688         this.progressDialog = new Roo.bootstrap.Modal({
37689             cls : 'roo-document-manager-progress-dialog',
37690             allow_close : false,
37691             animate : false,
37692             title : '',
37693             buttons : [
37694                 {
37695                     name  :'cancel',
37696                     weight : 'danger',
37697                     html : 'Cancel'
37698                 }
37699             ], 
37700             listeners : { 
37701                 btnclick : function() {
37702                     _this.uploadCancel();
37703                     this.hide();
37704                 }
37705             }
37706         });
37707          
37708         this.progressDialog.render(Roo.get(document.body));
37709          
37710         this.progress = new Roo.bootstrap.Progress({
37711             cls : 'roo-document-manager-progress',
37712             active : true,
37713             striped : true
37714         });
37715         
37716         this.progress.render(this.progressDialog.getChildContainer());
37717         
37718         this.progressBar = new Roo.bootstrap.ProgressBar({
37719             cls : 'roo-document-manager-progress-bar',
37720             aria_valuenow : 0,
37721             aria_valuemin : 0,
37722             aria_valuemax : 12,
37723             panel : 'success'
37724         });
37725         
37726         this.progressBar.render(this.progress.getChildContainer());
37727     },
37728     
37729     onUploaderClick : function(e)
37730     {
37731         e.preventDefault();
37732      
37733         if(this.fireEvent('beforeselectfile', this) != false){
37734             this.selectorEl.dom.click();
37735         }
37736         
37737     },
37738     
37739     onFileSelected : function(e)
37740     {
37741         e.preventDefault();
37742         
37743         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37744             return;
37745         }
37746         
37747         Roo.each(this.selectorEl.dom.files, function(file){
37748             if(this.fireEvent('inspect', this, file) != false){
37749                 this.files.push(file);
37750             }
37751         }, this);
37752         
37753         this.queue();
37754         
37755     },
37756     
37757     queue : function()
37758     {
37759         this.selectorEl.dom.value = '';
37760         
37761         if(!this.files || !this.files.length){
37762             return;
37763         }
37764         
37765         if(this.boxes > 0 && this.files.length > this.boxes){
37766             this.files = this.files.slice(0, this.boxes);
37767         }
37768         
37769         this.uploader.show();
37770         
37771         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37772             this.uploader.hide();
37773         }
37774         
37775         var _this = this;
37776         
37777         var files = [];
37778         
37779         var docs = [];
37780         
37781         Roo.each(this.files, function(file){
37782             
37783             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37784                 var f = this.renderPreview(file);
37785                 files.push(f);
37786                 return;
37787             }
37788             
37789             if(file.type.indexOf('image') != -1){
37790                 this.delegates.push(
37791                     (function(){
37792                         _this.process(file);
37793                     }).createDelegate(this)
37794                 );
37795         
37796                 return;
37797             }
37798             
37799             docs.push(
37800                 (function(){
37801                     _this.process(file);
37802                 }).createDelegate(this)
37803             );
37804             
37805         }, this);
37806         
37807         this.files = files;
37808         
37809         this.delegates = this.delegates.concat(docs);
37810         
37811         if(!this.delegates.length){
37812             this.refresh();
37813             return;
37814         }
37815         
37816         this.progressBar.aria_valuemax = this.delegates.length;
37817         
37818         this.arrange();
37819         
37820         return;
37821     },
37822     
37823     arrange : function()
37824     {
37825         if(!this.delegates.length){
37826             this.progressDialog.hide();
37827             this.refresh();
37828             return;
37829         }
37830         
37831         var delegate = this.delegates.shift();
37832         
37833         this.progressDialog.show();
37834         
37835         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37836         
37837         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37838         
37839         delegate();
37840     },
37841     
37842     refresh : function()
37843     {
37844         this.uploader.show();
37845         
37846         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37847             this.uploader.hide();
37848         }
37849         
37850         Roo.isTouch ? this.closable(false) : this.closable(true);
37851         
37852         this.fireEvent('refresh', this);
37853     },
37854     
37855     onRemove : function(e, el, o)
37856     {
37857         e.preventDefault();
37858         
37859         this.fireEvent('remove', this, o);
37860         
37861     },
37862     
37863     remove : function(o)
37864     {
37865         var files = [];
37866         
37867         Roo.each(this.files, function(file){
37868             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37869                 files.push(file);
37870                 return;
37871             }
37872
37873             o.target.remove();
37874
37875         }, this);
37876         
37877         this.files = files;
37878         
37879         this.refresh();
37880     },
37881     
37882     clear : function()
37883     {
37884         Roo.each(this.files, function(file){
37885             if(!file.target){
37886                 return;
37887             }
37888             
37889             file.target.remove();
37890
37891         }, this);
37892         
37893         this.files = [];
37894         
37895         this.refresh();
37896     },
37897     
37898     onClick : function(e, el, o)
37899     {
37900         e.preventDefault();
37901         
37902         this.fireEvent('click', this, o);
37903         
37904     },
37905     
37906     closable : function(closable)
37907     {
37908         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37909             
37910             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37911             
37912             if(closable){
37913                 el.show();
37914                 return;
37915             }
37916             
37917             el.hide();
37918             
37919         }, this);
37920     },
37921     
37922     xhrOnLoad : function(xhr)
37923     {
37924         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37925             el.remove();
37926         }, this);
37927         
37928         if (xhr.readyState !== 4) {
37929             this.arrange();
37930             this.fireEvent('exception', this, xhr);
37931             return;
37932         }
37933
37934         var response = Roo.decode(xhr.responseText);
37935         
37936         if(!response.success){
37937             this.arrange();
37938             this.fireEvent('exception', this, xhr);
37939             return;
37940         }
37941         
37942         var file = this.renderPreview(response.data);
37943         
37944         this.files.push(file);
37945         
37946         this.arrange();
37947         
37948         this.fireEvent('afterupload', this, xhr);
37949         
37950     },
37951     
37952     xhrOnError : function(xhr)
37953     {
37954         Roo.log('xhr on error');
37955         
37956         var response = Roo.decode(xhr.responseText);
37957           
37958         Roo.log(response);
37959         
37960         this.arrange();
37961     },
37962     
37963     process : function(file)
37964     {
37965         if(this.fireEvent('process', this, file) !== false){
37966             if(this.editable && file.type.indexOf('image') != -1){
37967                 this.fireEvent('edit', this, file);
37968                 return;
37969             }
37970
37971             this.uploadStart(file, false);
37972
37973             return;
37974         }
37975         
37976     },
37977     
37978     uploadStart : function(file, crop)
37979     {
37980         this.xhr = new XMLHttpRequest();
37981         
37982         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37983             this.arrange();
37984             return;
37985         }
37986         
37987         file.xhr = this.xhr;
37988             
37989         this.managerEl.createChild({
37990             tag : 'div',
37991             cls : 'roo-document-manager-loading',
37992             cn : [
37993                 {
37994                     tag : 'div',
37995                     tooltip : file.name,
37996                     cls : 'roo-document-manager-thumb',
37997                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37998                 }
37999             ]
38000
38001         });
38002
38003         this.xhr.open(this.method, this.url, true);
38004         
38005         var headers = {
38006             "Accept": "application/json",
38007             "Cache-Control": "no-cache",
38008             "X-Requested-With": "XMLHttpRequest"
38009         };
38010         
38011         for (var headerName in headers) {
38012             var headerValue = headers[headerName];
38013             if (headerValue) {
38014                 this.xhr.setRequestHeader(headerName, headerValue);
38015             }
38016         }
38017         
38018         var _this = this;
38019         
38020         this.xhr.onload = function()
38021         {
38022             _this.xhrOnLoad(_this.xhr);
38023         }
38024         
38025         this.xhr.onerror = function()
38026         {
38027             _this.xhrOnError(_this.xhr);
38028         }
38029         
38030         var formData = new FormData();
38031
38032         formData.append('returnHTML', 'NO');
38033         
38034         if(crop){
38035             formData.append('crop', crop);
38036         }
38037         
38038         formData.append(this.paramName, file, file.name);
38039         
38040         var options = {
38041             file : file, 
38042             manually : false
38043         };
38044         
38045         if(this.fireEvent('prepare', this, formData, options) != false){
38046             
38047             if(options.manually){
38048                 return;
38049             }
38050             
38051             this.xhr.send(formData);
38052             return;
38053         };
38054         
38055         this.uploadCancel();
38056     },
38057     
38058     uploadCancel : function()
38059     {
38060         if (this.xhr) {
38061             this.xhr.abort();
38062         }
38063         
38064         this.delegates = [];
38065         
38066         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38067             el.remove();
38068         }, this);
38069         
38070         this.arrange();
38071     },
38072     
38073     renderPreview : function(file)
38074     {
38075         if(typeof(file.target) != 'undefined' && file.target){
38076             return file;
38077         }
38078         
38079         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38080         
38081         var previewEl = this.managerEl.createChild({
38082             tag : 'div',
38083             cls : 'roo-document-manager-preview',
38084             cn : [
38085                 {
38086                     tag : 'div',
38087                     tooltip : file[this.toolTipName],
38088                     cls : 'roo-document-manager-thumb',
38089                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38090                 },
38091                 {
38092                     tag : 'button',
38093                     cls : 'close',
38094                     html : '<i class="fa fa-times-circle"></i>'
38095                 }
38096             ]
38097         });
38098
38099         var close = previewEl.select('button.close', true).first();
38100
38101         close.on('click', this.onRemove, this, file);
38102
38103         file.target = previewEl;
38104
38105         var image = previewEl.select('img', true).first();
38106         
38107         var _this = this;
38108         
38109         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38110         
38111         image.on('click', this.onClick, this, file);
38112         
38113         this.fireEvent('previewrendered', this, file);
38114         
38115         return file;
38116         
38117     },
38118     
38119     onPreviewLoad : function(file, image)
38120     {
38121         if(typeof(file.target) == 'undefined' || !file.target){
38122             return;
38123         }
38124         
38125         var width = image.dom.naturalWidth || image.dom.width;
38126         var height = image.dom.naturalHeight || image.dom.height;
38127         
38128         if(!this.previewResize) {
38129             return;
38130         }
38131         
38132         if(width > height){
38133             file.target.addClass('wide');
38134             return;
38135         }
38136         
38137         file.target.addClass('tall');
38138         return;
38139         
38140     },
38141     
38142     uploadFromSource : function(file, crop)
38143     {
38144         this.xhr = new XMLHttpRequest();
38145         
38146         this.managerEl.createChild({
38147             tag : 'div',
38148             cls : 'roo-document-manager-loading',
38149             cn : [
38150                 {
38151                     tag : 'div',
38152                     tooltip : file.name,
38153                     cls : 'roo-document-manager-thumb',
38154                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38155                 }
38156             ]
38157
38158         });
38159
38160         this.xhr.open(this.method, this.url, true);
38161         
38162         var headers = {
38163             "Accept": "application/json",
38164             "Cache-Control": "no-cache",
38165             "X-Requested-With": "XMLHttpRequest"
38166         };
38167         
38168         for (var headerName in headers) {
38169             var headerValue = headers[headerName];
38170             if (headerValue) {
38171                 this.xhr.setRequestHeader(headerName, headerValue);
38172             }
38173         }
38174         
38175         var _this = this;
38176         
38177         this.xhr.onload = function()
38178         {
38179             _this.xhrOnLoad(_this.xhr);
38180         }
38181         
38182         this.xhr.onerror = function()
38183         {
38184             _this.xhrOnError(_this.xhr);
38185         }
38186         
38187         var formData = new FormData();
38188
38189         formData.append('returnHTML', 'NO');
38190         
38191         formData.append('crop', crop);
38192         
38193         if(typeof(file.filename) != 'undefined'){
38194             formData.append('filename', file.filename);
38195         }
38196         
38197         if(typeof(file.mimetype) != 'undefined'){
38198             formData.append('mimetype', file.mimetype);
38199         }
38200         
38201         Roo.log(formData);
38202         
38203         if(this.fireEvent('prepare', this, formData) != false){
38204             this.xhr.send(formData);
38205         };
38206     }
38207 });
38208
38209 /*
38210 * Licence: LGPL
38211 */
38212
38213 /**
38214  * @class Roo.bootstrap.DocumentViewer
38215  * @extends Roo.bootstrap.Component
38216  * Bootstrap DocumentViewer class
38217  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38218  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38219  * 
38220  * @constructor
38221  * Create a new DocumentViewer
38222  * @param {Object} config The config object
38223  */
38224
38225 Roo.bootstrap.DocumentViewer = function(config){
38226     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38227     
38228     this.addEvents({
38229         /**
38230          * @event initial
38231          * Fire after initEvent
38232          * @param {Roo.bootstrap.DocumentViewer} this
38233          */
38234         "initial" : true,
38235         /**
38236          * @event click
38237          * Fire after click
38238          * @param {Roo.bootstrap.DocumentViewer} this
38239          */
38240         "click" : true,
38241         /**
38242          * @event download
38243          * Fire after download button
38244          * @param {Roo.bootstrap.DocumentViewer} this
38245          */
38246         "download" : true,
38247         /**
38248          * @event trash
38249          * Fire after trash button
38250          * @param {Roo.bootstrap.DocumentViewer} this
38251          */
38252         "trash" : true
38253         
38254     });
38255 };
38256
38257 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38258     
38259     showDownload : true,
38260     
38261     showTrash : true,
38262     
38263     getAutoCreate : function()
38264     {
38265         var cfg = {
38266             tag : 'div',
38267             cls : 'roo-document-viewer',
38268             cn : [
38269                 {
38270                     tag : 'div',
38271                     cls : 'roo-document-viewer-body',
38272                     cn : [
38273                         {
38274                             tag : 'div',
38275                             cls : 'roo-document-viewer-thumb',
38276                             cn : [
38277                                 {
38278                                     tag : 'img',
38279                                     cls : 'roo-document-viewer-image'
38280                                 }
38281                             ]
38282                         }
38283                     ]
38284                 },
38285                 {
38286                     tag : 'div',
38287                     cls : 'roo-document-viewer-footer',
38288                     cn : {
38289                         tag : 'div',
38290                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38291                         cn : [
38292                             {
38293                                 tag : 'div',
38294                                 cls : 'btn-group roo-document-viewer-download',
38295                                 cn : [
38296                                     {
38297                                         tag : 'button',
38298                                         cls : 'btn btn-default',
38299                                         html : '<i class="fa fa-download"></i>'
38300                                     }
38301                                 ]
38302                             },
38303                             {
38304                                 tag : 'div',
38305                                 cls : 'btn-group roo-document-viewer-trash',
38306                                 cn : [
38307                                     {
38308                                         tag : 'button',
38309                                         cls : 'btn btn-default',
38310                                         html : '<i class="fa fa-trash"></i>'
38311                                     }
38312                                 ]
38313                             }
38314                         ]
38315                     }
38316                 }
38317             ]
38318         };
38319         
38320         return cfg;
38321     },
38322     
38323     initEvents : function()
38324     {
38325         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38326         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38327         
38328         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38329         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38330         
38331         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38332         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38333         
38334         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38335         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38336         
38337         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38338         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38339         
38340         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38341         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38342         
38343         this.bodyEl.on('click', this.onClick, this);
38344         this.downloadBtn.on('click', this.onDownload, this);
38345         this.trashBtn.on('click', this.onTrash, this);
38346         
38347         this.downloadBtn.hide();
38348         this.trashBtn.hide();
38349         
38350         if(this.showDownload){
38351             this.downloadBtn.show();
38352         }
38353         
38354         if(this.showTrash){
38355             this.trashBtn.show();
38356         }
38357         
38358         if(!this.showDownload && !this.showTrash) {
38359             this.footerEl.hide();
38360         }
38361         
38362     },
38363     
38364     initial : function()
38365     {
38366         this.fireEvent('initial', this);
38367         
38368     },
38369     
38370     onClick : function(e)
38371     {
38372         e.preventDefault();
38373         
38374         this.fireEvent('click', this);
38375     },
38376     
38377     onDownload : function(e)
38378     {
38379         e.preventDefault();
38380         
38381         this.fireEvent('download', this);
38382     },
38383     
38384     onTrash : function(e)
38385     {
38386         e.preventDefault();
38387         
38388         this.fireEvent('trash', this);
38389     }
38390     
38391 });
38392 /*
38393  * - LGPL
38394  *
38395  * FieldLabel
38396  * 
38397  */
38398
38399 /**
38400  * @class Roo.bootstrap.form.FieldLabel
38401  * @extends Roo.bootstrap.Component
38402  * Bootstrap FieldLabel class
38403  * @cfg {String} html contents of the element
38404  * @cfg {String} tag tag of the element default label
38405  * @cfg {String} cls class of the element
38406  * @cfg {String} target label target 
38407  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38408  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38409  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38410  * @cfg {String} iconTooltip default "This field is required"
38411  * @cfg {String} indicatorpos (left|right) default left
38412  * 
38413  * @constructor
38414  * Create a new FieldLabel
38415  * @param {Object} config The config object
38416  */
38417
38418 Roo.bootstrap.form.FieldLabel = function(config){
38419     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38420     
38421     this.addEvents({
38422             /**
38423              * @event invalid
38424              * Fires after the field has been marked as invalid.
38425              * @param {Roo.form.FieldLabel} this
38426              * @param {String} msg The validation message
38427              */
38428             invalid : true,
38429             /**
38430              * @event valid
38431              * Fires after the field has been validated with no errors.
38432              * @param {Roo.form.FieldLabel} this
38433              */
38434             valid : true
38435         });
38436 };
38437
38438 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38439     
38440     tag: 'label',
38441     cls: '',
38442     html: '',
38443     target: '',
38444     allowBlank : true,
38445     invalidClass : 'has-warning',
38446     validClass : 'has-success',
38447     iconTooltip : 'This field is required',
38448     indicatorpos : 'left',
38449     
38450     getAutoCreate : function(){
38451         
38452         var cls = "";
38453         if (!this.allowBlank) {
38454             cls  = "visible";
38455         }
38456         
38457         var cfg = {
38458             tag : this.tag,
38459             cls : 'roo-bootstrap-field-label ' + this.cls,
38460             for : this.target,
38461             cn : [
38462                 {
38463                     tag : 'i',
38464                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38465                     tooltip : this.iconTooltip
38466                 },
38467                 {
38468                     tag : 'span',
38469                     html : this.html
38470                 }
38471             ] 
38472         };
38473         
38474         if(this.indicatorpos == 'right'){
38475             var cfg = {
38476                 tag : this.tag,
38477                 cls : 'roo-bootstrap-field-label ' + this.cls,
38478                 for : this.target,
38479                 cn : [
38480                     {
38481                         tag : 'span',
38482                         html : this.html
38483                     },
38484                     {
38485                         tag : 'i',
38486                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38487                         tooltip : this.iconTooltip
38488                     }
38489                 ] 
38490             };
38491         }
38492         
38493         return cfg;
38494     },
38495     
38496     initEvents: function() 
38497     {
38498         Roo.bootstrap.Element.superclass.initEvents.call(this);
38499         
38500         this.indicator = this.indicatorEl();
38501         
38502         if(this.indicator){
38503             this.indicator.removeClass('visible');
38504             this.indicator.addClass('invisible');
38505         }
38506         
38507         Roo.bootstrap.form.FieldLabel.register(this);
38508     },
38509     
38510     indicatorEl : function()
38511     {
38512         var indicator = this.el.select('i.roo-required-indicator',true).first();
38513         
38514         if(!indicator){
38515             return false;
38516         }
38517         
38518         return indicator;
38519         
38520     },
38521     
38522     /**
38523      * Mark this field as valid
38524      */
38525     markValid : function()
38526     {
38527         if(this.indicator){
38528             this.indicator.removeClass('visible');
38529             this.indicator.addClass('invisible');
38530         }
38531         if (Roo.bootstrap.version == 3) {
38532             this.el.removeClass(this.invalidClass);
38533             this.el.addClass(this.validClass);
38534         } else {
38535             this.el.removeClass('is-invalid');
38536             this.el.addClass('is-valid');
38537         }
38538         
38539         
38540         this.fireEvent('valid', this);
38541     },
38542     
38543     /**
38544      * Mark this field as invalid
38545      * @param {String} msg The validation message
38546      */
38547     markInvalid : function(msg)
38548     {
38549         if(this.indicator){
38550             this.indicator.removeClass('invisible');
38551             this.indicator.addClass('visible');
38552         }
38553           if (Roo.bootstrap.version == 3) {
38554             this.el.removeClass(this.validClass);
38555             this.el.addClass(this.invalidClass);
38556         } else {
38557             this.el.removeClass('is-valid');
38558             this.el.addClass('is-invalid');
38559         }
38560         
38561         
38562         this.fireEvent('invalid', this, msg);
38563     }
38564     
38565    
38566 });
38567
38568 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38569     
38570     groups: {},
38571     
38572      /**
38573     * register a FieldLabel Group
38574     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38575     */
38576     register : function(label)
38577     {
38578         if(this.groups.hasOwnProperty(label.target)){
38579             return;
38580         }
38581      
38582         this.groups[label.target] = label;
38583         
38584     },
38585     /**
38586     * fetch a FieldLabel Group based on the target
38587     * @param {string} target
38588     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38589     */
38590     get: function(target) {
38591         if (typeof(this.groups[target]) == 'undefined') {
38592             return false;
38593         }
38594         
38595         return this.groups[target] ;
38596     }
38597 });
38598
38599  
38600
38601  /*
38602  * - LGPL
38603  *
38604  * page DateSplitField.
38605  * 
38606  */
38607
38608
38609 /**
38610  * @class Roo.bootstrap.form.DateSplitField
38611  * @extends Roo.bootstrap.Component
38612  * Bootstrap DateSplitField class
38613  * @cfg {string} fieldLabel - the label associated
38614  * @cfg {Number} labelWidth set the width of label (0-12)
38615  * @cfg {String} labelAlign (top|left)
38616  * @cfg {Boolean} dayAllowBlank (true|false) default false
38617  * @cfg {Boolean} monthAllowBlank (true|false) default false
38618  * @cfg {Boolean} yearAllowBlank (true|false) default false
38619  * @cfg {string} dayPlaceholder 
38620  * @cfg {string} monthPlaceholder
38621  * @cfg {string} yearPlaceholder
38622  * @cfg {string} dayFormat default 'd'
38623  * @cfg {string} monthFormat default 'm'
38624  * @cfg {string} yearFormat default 'Y'
38625  * @cfg {Number} labellg set the width of label (1-12)
38626  * @cfg {Number} labelmd set the width of label (1-12)
38627  * @cfg {Number} labelsm set the width of label (1-12)
38628  * @cfg {Number} labelxs set the width of label (1-12)
38629
38630  *     
38631  * @constructor
38632  * Create a new DateSplitField
38633  * @param {Object} config The config object
38634  */
38635
38636 Roo.bootstrap.form.DateSplitField = function(config){
38637     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38638     
38639     this.addEvents({
38640         // raw events
38641          /**
38642          * @event years
38643          * getting the data of years
38644          * @param {Roo.bootstrap.form.DateSplitField} this
38645          * @param {Object} years
38646          */
38647         "years" : true,
38648         /**
38649          * @event days
38650          * getting the data of days
38651          * @param {Roo.bootstrap.form.DateSplitField} this
38652          * @param {Object} days
38653          */
38654         "days" : true,
38655         /**
38656          * @event invalid
38657          * Fires after the field has been marked as invalid.
38658          * @param {Roo.form.Field} this
38659          * @param {String} msg The validation message
38660          */
38661         invalid : true,
38662        /**
38663          * @event valid
38664          * Fires after the field has been validated with no errors.
38665          * @param {Roo.form.Field} this
38666          */
38667         valid : true
38668     });
38669 };
38670
38671 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38672     
38673     fieldLabel : '',
38674     labelAlign : 'top',
38675     labelWidth : 3,
38676     dayAllowBlank : false,
38677     monthAllowBlank : false,
38678     yearAllowBlank : false,
38679     dayPlaceholder : '',
38680     monthPlaceholder : '',
38681     yearPlaceholder : '',
38682     dayFormat : 'd',
38683     monthFormat : 'm',
38684     yearFormat : 'Y',
38685     isFormField : true,
38686     labellg : 0,
38687     labelmd : 0,
38688     labelsm : 0,
38689     labelxs : 0,
38690     
38691     getAutoCreate : function()
38692     {
38693         var cfg = {
38694             tag : 'div',
38695             cls : 'row roo-date-split-field-group',
38696             cn : [
38697                 {
38698                     tag : 'input',
38699                     type : 'hidden',
38700                     cls : 'form-hidden-field roo-date-split-field-group-value',
38701                     name : this.name
38702                 }
38703             ]
38704         };
38705         
38706         var labelCls = 'col-md-12';
38707         var contentCls = 'col-md-4';
38708         
38709         if(this.fieldLabel){
38710             
38711             var label = {
38712                 tag : 'div',
38713                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38714                 cn : [
38715                     {
38716                         tag : 'label',
38717                         html : this.fieldLabel
38718                     }
38719                 ]
38720             };
38721             
38722             if(this.labelAlign == 'left'){
38723             
38724                 if(this.labelWidth > 12){
38725                     label.style = "width: " + this.labelWidth + 'px';
38726                 }
38727
38728                 if(this.labelWidth < 13 && this.labelmd == 0){
38729                     this.labelmd = this.labelWidth;
38730                 }
38731
38732                 if(this.labellg > 0){
38733                     labelCls = ' col-lg-' + this.labellg;
38734                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38735                 }
38736
38737                 if(this.labelmd > 0){
38738                     labelCls = ' col-md-' + this.labelmd;
38739                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38740                 }
38741
38742                 if(this.labelsm > 0){
38743                     labelCls = ' col-sm-' + this.labelsm;
38744                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38745                 }
38746
38747                 if(this.labelxs > 0){
38748                     labelCls = ' col-xs-' + this.labelxs;
38749                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38750                 }
38751             }
38752             
38753             label.cls += ' ' + labelCls;
38754             
38755             cfg.cn.push(label);
38756         }
38757         
38758         Roo.each(['day', 'month', 'year'], function(t){
38759             cfg.cn.push({
38760                 tag : 'div',
38761                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38762             });
38763         }, this);
38764         
38765         return cfg;
38766     },
38767     
38768     inputEl: function ()
38769     {
38770         return this.el.select('.roo-date-split-field-group-value', true).first();
38771     },
38772     
38773     onRender : function(ct, position) 
38774     {
38775         var _this = this;
38776         
38777         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38778         
38779         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38780         
38781         this.dayField = new Roo.bootstrap.form.ComboBox({
38782             allowBlank : this.dayAllowBlank,
38783             alwaysQuery : true,
38784             displayField : 'value',
38785             editable : false,
38786             fieldLabel : '',
38787             forceSelection : true,
38788             mode : 'local',
38789             placeholder : this.dayPlaceholder,
38790             selectOnFocus : true,
38791             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38792             triggerAction : 'all',
38793             typeAhead : true,
38794             valueField : 'value',
38795             store : new Roo.data.SimpleStore({
38796                 data : (function() {    
38797                     var days = [];
38798                     _this.fireEvent('days', _this, days);
38799                     return days;
38800                 })(),
38801                 fields : [ 'value' ]
38802             }),
38803             listeners : {
38804                 select : function (_self, record, index)
38805                 {
38806                     _this.setValue(_this.getValue());
38807                 }
38808             }
38809         });
38810
38811         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38812         
38813         this.monthField = new Roo.bootstrap.form.MonthField({
38814             after : '<i class=\"fa fa-calendar\"></i>',
38815             allowBlank : this.monthAllowBlank,
38816             placeholder : this.monthPlaceholder,
38817             readOnly : true,
38818             listeners : {
38819                 render : function (_self)
38820                 {
38821                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38822                         e.preventDefault();
38823                         _self.focus();
38824                     });
38825                 },
38826                 select : function (_self, oldvalue, newvalue)
38827                 {
38828                     _this.setValue(_this.getValue());
38829                 }
38830             }
38831         });
38832         
38833         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38834         
38835         this.yearField = new Roo.bootstrap.form.ComboBox({
38836             allowBlank : this.yearAllowBlank,
38837             alwaysQuery : true,
38838             displayField : 'value',
38839             editable : false,
38840             fieldLabel : '',
38841             forceSelection : true,
38842             mode : 'local',
38843             placeholder : this.yearPlaceholder,
38844             selectOnFocus : true,
38845             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38846             triggerAction : 'all',
38847             typeAhead : true,
38848             valueField : 'value',
38849             store : new Roo.data.SimpleStore({
38850                 data : (function() {
38851                     var years = [];
38852                     _this.fireEvent('years', _this, years);
38853                     return years;
38854                 })(),
38855                 fields : [ 'value' ]
38856             }),
38857             listeners : {
38858                 select : function (_self, record, index)
38859                 {
38860                     _this.setValue(_this.getValue());
38861                 }
38862             }
38863         });
38864
38865         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38866     },
38867     
38868     setValue : function(v, format)
38869     {
38870         this.inputEl.dom.value = v;
38871         
38872         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38873         
38874         var d = Date.parseDate(v, f);
38875         
38876         if(!d){
38877             this.validate();
38878             return;
38879         }
38880         
38881         this.setDay(d.format(this.dayFormat));
38882         this.setMonth(d.format(this.monthFormat));
38883         this.setYear(d.format(this.yearFormat));
38884         
38885         this.validate();
38886         
38887         return;
38888     },
38889     
38890     setDay : function(v)
38891     {
38892         this.dayField.setValue(v);
38893         this.inputEl.dom.value = this.getValue();
38894         this.validate();
38895         return;
38896     },
38897     
38898     setMonth : function(v)
38899     {
38900         this.monthField.setValue(v, true);
38901         this.inputEl.dom.value = this.getValue();
38902         this.validate();
38903         return;
38904     },
38905     
38906     setYear : function(v)
38907     {
38908         this.yearField.setValue(v);
38909         this.inputEl.dom.value = this.getValue();
38910         this.validate();
38911         return;
38912     },
38913     
38914     getDay : function()
38915     {
38916         return this.dayField.getValue();
38917     },
38918     
38919     getMonth : function()
38920     {
38921         return this.monthField.getValue();
38922     },
38923     
38924     getYear : function()
38925     {
38926         return this.yearField.getValue();
38927     },
38928     
38929     getValue : function()
38930     {
38931         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38932         
38933         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38934         
38935         return date;
38936     },
38937     
38938     reset : function()
38939     {
38940         this.setDay('');
38941         this.setMonth('');
38942         this.setYear('');
38943         this.inputEl.dom.value = '';
38944         this.validate();
38945         return;
38946     },
38947     
38948     validate : function()
38949     {
38950         var d = this.dayField.validate();
38951         var m = this.monthField.validate();
38952         var y = this.yearField.validate();
38953         
38954         var valid = true;
38955         
38956         if(
38957                 (!this.dayAllowBlank && !d) ||
38958                 (!this.monthAllowBlank && !m) ||
38959                 (!this.yearAllowBlank && !y)
38960         ){
38961             valid = false;
38962         }
38963         
38964         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38965             return valid;
38966         }
38967         
38968         if(valid){
38969             this.markValid();
38970             return valid;
38971         }
38972         
38973         this.markInvalid();
38974         
38975         return valid;
38976     },
38977     
38978     markValid : function()
38979     {
38980         
38981         var label = this.el.select('label', true).first();
38982         var icon = this.el.select('i.fa-star', true).first();
38983
38984         if(label && icon){
38985             icon.remove();
38986         }
38987         
38988         this.fireEvent('valid', this);
38989     },
38990     
38991      /**
38992      * Mark this field as invalid
38993      * @param {String} msg The validation message
38994      */
38995     markInvalid : function(msg)
38996     {
38997         
38998         var label = this.el.select('label', true).first();
38999         var icon = this.el.select('i.fa-star', true).first();
39000
39001         if(label && !icon){
39002             this.el.select('.roo-date-split-field-label', true).createChild({
39003                 tag : 'i',
39004                 cls : 'text-danger fa fa-lg fa-star',
39005                 tooltip : 'This field is required',
39006                 style : 'margin-right:5px;'
39007             }, label, true);
39008         }
39009         
39010         this.fireEvent('invalid', this, msg);
39011     },
39012     
39013     clearInvalid : function()
39014     {
39015         var label = this.el.select('label', true).first();
39016         var icon = this.el.select('i.fa-star', true).first();
39017
39018         if(label && icon){
39019             icon.remove();
39020         }
39021         
39022         this.fireEvent('valid', this);
39023     },
39024     
39025     getName: function()
39026     {
39027         return this.name;
39028     }
39029     
39030 });
39031
39032  
39033
39034 /**
39035  * @class Roo.bootstrap.LayoutMasonry
39036  * @extends Roo.bootstrap.Component
39037  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39038  * Bootstrap Layout Masonry class
39039  *
39040  * This is based on 
39041  * http://masonry.desandro.com
39042  *
39043  * The idea is to render all the bricks based on vertical width...
39044  *
39045  * The original code extends 'outlayer' - we might need to use that....
39046
39047  * @constructor
39048  * Create a new Element
39049  * @param {Object} config The config object
39050  */
39051
39052 Roo.bootstrap.LayoutMasonry = function(config){
39053     
39054     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39055     
39056     this.bricks = [];
39057     
39058     Roo.bootstrap.LayoutMasonry.register(this);
39059     
39060     this.addEvents({
39061         // raw events
39062         /**
39063          * @event layout
39064          * Fire after layout the items
39065          * @param {Roo.bootstrap.LayoutMasonry} this
39066          * @param {Roo.EventObject} e
39067          */
39068         "layout" : true
39069     });
39070     
39071 };
39072
39073 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39074     
39075     /**
39076      * @cfg {Boolean} isLayoutInstant = no animation?
39077      */   
39078     isLayoutInstant : false, // needed?
39079    
39080     /**
39081      * @cfg {Number} boxWidth  width of the columns
39082      */   
39083     boxWidth : 450,
39084     
39085       /**
39086      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39087      */   
39088     boxHeight : 0,
39089     
39090     /**
39091      * @cfg {Number} padWidth padding below box..
39092      */   
39093     padWidth : 10, 
39094     
39095     /**
39096      * @cfg {Number} gutter gutter width..
39097      */   
39098     gutter : 10,
39099     
39100      /**
39101      * @cfg {Number} maxCols maximum number of columns
39102      */   
39103     
39104     maxCols: 0,
39105     
39106     /**
39107      * @cfg {Boolean} isAutoInitial defalut true
39108      */   
39109     isAutoInitial : true, 
39110     
39111     containerWidth: 0,
39112     
39113     /**
39114      * @cfg {Boolean} isHorizontal defalut false
39115      */   
39116     isHorizontal : false, 
39117
39118     currentSize : null,
39119     
39120     tag: 'div',
39121     
39122     cls: '',
39123     
39124     bricks: null, //CompositeElement
39125     
39126     cols : 1,
39127     
39128     _isLayoutInited : false,
39129     
39130 //    isAlternative : false, // only use for vertical layout...
39131     
39132     /**
39133      * @cfg {Number} alternativePadWidth padding below box..
39134      */   
39135     alternativePadWidth : 50,
39136     
39137     selectedBrick : [],
39138     
39139     getAutoCreate : function(){
39140         
39141         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39142         
39143         var cfg = {
39144             tag: this.tag,
39145             cls: 'blog-masonary-wrapper ' + this.cls,
39146             cn : {
39147                 cls : 'mas-boxes masonary'
39148             }
39149         };
39150         
39151         return cfg;
39152     },
39153     
39154     getChildContainer: function( )
39155     {
39156         if (this.boxesEl) {
39157             return this.boxesEl;
39158         }
39159         
39160         this.boxesEl = this.el.select('.mas-boxes').first();
39161         
39162         return this.boxesEl;
39163     },
39164     
39165     
39166     initEvents : function()
39167     {
39168         var _this = this;
39169         
39170         if(this.isAutoInitial){
39171             Roo.log('hook children rendered');
39172             this.on('childrenrendered', function() {
39173                 Roo.log('children rendered');
39174                 _this.initial();
39175             } ,this);
39176         }
39177     },
39178     
39179     initial : function()
39180     {
39181         this.selectedBrick = [];
39182         
39183         this.currentSize = this.el.getBox(true);
39184         
39185         Roo.EventManager.onWindowResize(this.resize, this); 
39186
39187         if(!this.isAutoInitial){
39188             this.layout();
39189             return;
39190         }
39191         
39192         this.layout();
39193         
39194         return;
39195         //this.layout.defer(500,this);
39196         
39197     },
39198     
39199     resize : function()
39200     {
39201         var cs = this.el.getBox(true);
39202         
39203         if (
39204                 this.currentSize.width == cs.width && 
39205                 this.currentSize.x == cs.x && 
39206                 this.currentSize.height == cs.height && 
39207                 this.currentSize.y == cs.y 
39208         ) {
39209             Roo.log("no change in with or X or Y");
39210             return;
39211         }
39212         
39213         this.currentSize = cs;
39214         
39215         this.layout();
39216         
39217     },
39218     
39219     layout : function()
39220     {   
39221         this._resetLayout();
39222         
39223         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39224         
39225         this.layoutItems( isInstant );
39226       
39227         this._isLayoutInited = true;
39228         
39229         this.fireEvent('layout', this);
39230         
39231     },
39232     
39233     _resetLayout : function()
39234     {
39235         if(this.isHorizontal){
39236             this.horizontalMeasureColumns();
39237             return;
39238         }
39239         
39240         this.verticalMeasureColumns();
39241         
39242     },
39243     
39244     verticalMeasureColumns : function()
39245     {
39246         this.getContainerWidth();
39247         
39248 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39249 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39250 //            return;
39251 //        }
39252         
39253         var boxWidth = this.boxWidth + this.padWidth;
39254         
39255         if(this.containerWidth < this.boxWidth){
39256             boxWidth = this.containerWidth
39257         }
39258         
39259         var containerWidth = this.containerWidth;
39260         
39261         var cols = Math.floor(containerWidth / boxWidth);
39262         
39263         this.cols = Math.max( cols, 1 );
39264         
39265         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39266         
39267         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39268         
39269         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39270         
39271         this.colWidth = boxWidth + avail - this.padWidth;
39272         
39273         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39274         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39275     },
39276     
39277     horizontalMeasureColumns : function()
39278     {
39279         this.getContainerWidth();
39280         
39281         var boxWidth = this.boxWidth;
39282         
39283         if(this.containerWidth < boxWidth){
39284             boxWidth = this.containerWidth;
39285         }
39286         
39287         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39288         
39289         this.el.setHeight(boxWidth);
39290         
39291     },
39292     
39293     getContainerWidth : function()
39294     {
39295         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39296     },
39297     
39298     layoutItems : function( isInstant )
39299     {
39300         Roo.log(this.bricks);
39301         
39302         var items = Roo.apply([], this.bricks);
39303         
39304         if(this.isHorizontal){
39305             this._horizontalLayoutItems( items , isInstant );
39306             return;
39307         }
39308         
39309 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39310 //            this._verticalAlternativeLayoutItems( items , isInstant );
39311 //            return;
39312 //        }
39313         
39314         this._verticalLayoutItems( items , isInstant );
39315         
39316     },
39317     
39318     _verticalLayoutItems : function ( items , isInstant)
39319     {
39320         if ( !items || !items.length ) {
39321             return;
39322         }
39323         
39324         var standard = [
39325             ['xs', 'xs', 'xs', 'tall'],
39326             ['xs', 'xs', 'tall'],
39327             ['xs', 'xs', 'sm'],
39328             ['xs', 'xs', 'xs'],
39329             ['xs', 'tall'],
39330             ['xs', 'sm'],
39331             ['xs', 'xs'],
39332             ['xs'],
39333             
39334             ['sm', 'xs', 'xs'],
39335             ['sm', 'xs'],
39336             ['sm'],
39337             
39338             ['tall', 'xs', 'xs', 'xs'],
39339             ['tall', 'xs', 'xs'],
39340             ['tall', 'xs'],
39341             ['tall']
39342             
39343         ];
39344         
39345         var queue = [];
39346         
39347         var boxes = [];
39348         
39349         var box = [];
39350         
39351         Roo.each(items, function(item, k){
39352             
39353             switch (item.size) {
39354                 // these layouts take up a full box,
39355                 case 'md' :
39356                 case 'md-left' :
39357                 case 'md-right' :
39358                 case 'wide' :
39359                     
39360                     if(box.length){
39361                         boxes.push(box);
39362                         box = [];
39363                     }
39364                     
39365                     boxes.push([item]);
39366                     
39367                     break;
39368                     
39369                 case 'xs' :
39370                 case 'sm' :
39371                 case 'tall' :
39372                     
39373                     box.push(item);
39374                     
39375                     break;
39376                 default :
39377                     break;
39378                     
39379             }
39380             
39381         }, this);
39382         
39383         if(box.length){
39384             boxes.push(box);
39385             box = [];
39386         }
39387         
39388         var filterPattern = function(box, length)
39389         {
39390             if(!box.length){
39391                 return;
39392             }
39393             
39394             var match = false;
39395             
39396             var pattern = box.slice(0, length);
39397             
39398             var format = [];
39399             
39400             Roo.each(pattern, function(i){
39401                 format.push(i.size);
39402             }, this);
39403             
39404             Roo.each(standard, function(s){
39405                 
39406                 if(String(s) != String(format)){
39407                     return;
39408                 }
39409                 
39410                 match = true;
39411                 return false;
39412                 
39413             }, this);
39414             
39415             if(!match && length == 1){
39416                 return;
39417             }
39418             
39419             if(!match){
39420                 filterPattern(box, length - 1);
39421                 return;
39422             }
39423                 
39424             queue.push(pattern);
39425
39426             box = box.slice(length, box.length);
39427
39428             filterPattern(box, 4);
39429
39430             return;
39431             
39432         }
39433         
39434         Roo.each(boxes, function(box, k){
39435             
39436             if(!box.length){
39437                 return;
39438             }
39439             
39440             if(box.length == 1){
39441                 queue.push(box);
39442                 return;
39443             }
39444             
39445             filterPattern(box, 4);
39446             
39447         }, this);
39448         
39449         this._processVerticalLayoutQueue( queue, isInstant );
39450         
39451     },
39452     
39453 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39454 //    {
39455 //        if ( !items || !items.length ) {
39456 //            return;
39457 //        }
39458 //
39459 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39460 //        
39461 //    },
39462     
39463     _horizontalLayoutItems : function ( items , isInstant)
39464     {
39465         if ( !items || !items.length || items.length < 3) {
39466             return;
39467         }
39468         
39469         items.reverse();
39470         
39471         var eItems = items.slice(0, 3);
39472         
39473         items = items.slice(3, items.length);
39474         
39475         var standard = [
39476             ['xs', 'xs', 'xs', 'wide'],
39477             ['xs', 'xs', 'wide'],
39478             ['xs', 'xs', 'sm'],
39479             ['xs', 'xs', 'xs'],
39480             ['xs', 'wide'],
39481             ['xs', 'sm'],
39482             ['xs', 'xs'],
39483             ['xs'],
39484             
39485             ['sm', 'xs', 'xs'],
39486             ['sm', 'xs'],
39487             ['sm'],
39488             
39489             ['wide', 'xs', 'xs', 'xs'],
39490             ['wide', 'xs', 'xs'],
39491             ['wide', 'xs'],
39492             ['wide'],
39493             
39494             ['wide-thin']
39495         ];
39496         
39497         var queue = [];
39498         
39499         var boxes = [];
39500         
39501         var box = [];
39502         
39503         Roo.each(items, function(item, k){
39504             
39505             switch (item.size) {
39506                 case 'md' :
39507                 case 'md-left' :
39508                 case 'md-right' :
39509                 case 'tall' :
39510                     
39511                     if(box.length){
39512                         boxes.push(box);
39513                         box = [];
39514                     }
39515                     
39516                     boxes.push([item]);
39517                     
39518                     break;
39519                     
39520                 case 'xs' :
39521                 case 'sm' :
39522                 case 'wide' :
39523                 case 'wide-thin' :
39524                     
39525                     box.push(item);
39526                     
39527                     break;
39528                 default :
39529                     break;
39530                     
39531             }
39532             
39533         }, this);
39534         
39535         if(box.length){
39536             boxes.push(box);
39537             box = [];
39538         }
39539         
39540         var filterPattern = function(box, length)
39541         {
39542             if(!box.length){
39543                 return;
39544             }
39545             
39546             var match = false;
39547             
39548             var pattern = box.slice(0, length);
39549             
39550             var format = [];
39551             
39552             Roo.each(pattern, function(i){
39553                 format.push(i.size);
39554             }, this);
39555             
39556             Roo.each(standard, function(s){
39557                 
39558                 if(String(s) != String(format)){
39559                     return;
39560                 }
39561                 
39562                 match = true;
39563                 return false;
39564                 
39565             }, this);
39566             
39567             if(!match && length == 1){
39568                 return;
39569             }
39570             
39571             if(!match){
39572                 filterPattern(box, length - 1);
39573                 return;
39574             }
39575                 
39576             queue.push(pattern);
39577
39578             box = box.slice(length, box.length);
39579
39580             filterPattern(box, 4);
39581
39582             return;
39583             
39584         }
39585         
39586         Roo.each(boxes, function(box, k){
39587             
39588             if(!box.length){
39589                 return;
39590             }
39591             
39592             if(box.length == 1){
39593                 queue.push(box);
39594                 return;
39595             }
39596             
39597             filterPattern(box, 4);
39598             
39599         }, this);
39600         
39601         
39602         var prune = [];
39603         
39604         var pos = this.el.getBox(true);
39605         
39606         var minX = pos.x;
39607         
39608         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39609         
39610         var hit_end = false;
39611         
39612         Roo.each(queue, function(box){
39613             
39614             if(hit_end){
39615                 
39616                 Roo.each(box, function(b){
39617                 
39618                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39619                     b.el.hide();
39620
39621                 }, this);
39622
39623                 return;
39624             }
39625             
39626             var mx = 0;
39627             
39628             Roo.each(box, function(b){
39629                 
39630                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39631                 b.el.show();
39632
39633                 mx = Math.max(mx, b.x);
39634                 
39635             }, this);
39636             
39637             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39638             
39639             if(maxX < minX){
39640                 
39641                 Roo.each(box, function(b){
39642                 
39643                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39644                     b.el.hide();
39645                     
39646                 }, this);
39647                 
39648                 hit_end = true;
39649                 
39650                 return;
39651             }
39652             
39653             prune.push(box);
39654             
39655         }, this);
39656         
39657         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39658     },
39659     
39660     /** Sets position of item in DOM
39661     * @param {Element} item
39662     * @param {Number} x - horizontal position
39663     * @param {Number} y - vertical position
39664     * @param {Boolean} isInstant - disables transitions
39665     */
39666     _processVerticalLayoutQueue : function( queue, isInstant )
39667     {
39668         var pos = this.el.getBox(true);
39669         var x = pos.x;
39670         var y = pos.y;
39671         var maxY = [];
39672         
39673         for (var i = 0; i < this.cols; i++){
39674             maxY[i] = pos.y;
39675         }
39676         
39677         Roo.each(queue, function(box, k){
39678             
39679             var col = k % this.cols;
39680             
39681             Roo.each(box, function(b,kk){
39682                 
39683                 b.el.position('absolute');
39684                 
39685                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39686                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39687                 
39688                 if(b.size == 'md-left' || b.size == 'md-right'){
39689                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39690                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39691                 }
39692                 
39693                 b.el.setWidth(width);
39694                 b.el.setHeight(height);
39695                 // iframe?
39696                 b.el.select('iframe',true).setSize(width,height);
39697                 
39698             }, this);
39699             
39700             for (var i = 0; i < this.cols; i++){
39701                 
39702                 if(maxY[i] < maxY[col]){
39703                     col = i;
39704                     continue;
39705                 }
39706                 
39707                 col = Math.min(col, i);
39708                 
39709             }
39710             
39711             x = pos.x + col * (this.colWidth + this.padWidth);
39712             
39713             y = maxY[col];
39714             
39715             var positions = [];
39716             
39717             switch (box.length){
39718                 case 1 :
39719                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39720                     break;
39721                 case 2 :
39722                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39723                     break;
39724                 case 3 :
39725                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39726                     break;
39727                 case 4 :
39728                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39729                     break;
39730                 default :
39731                     break;
39732             }
39733             
39734             Roo.each(box, function(b,kk){
39735                 
39736                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39737                 
39738                 var sz = b.el.getSize();
39739                 
39740                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39741                 
39742             }, this);
39743             
39744         }, this);
39745         
39746         var mY = 0;
39747         
39748         for (var i = 0; i < this.cols; i++){
39749             mY = Math.max(mY, maxY[i]);
39750         }
39751         
39752         this.el.setHeight(mY - pos.y);
39753         
39754     },
39755     
39756 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39757 //    {
39758 //        var pos = this.el.getBox(true);
39759 //        var x = pos.x;
39760 //        var y = pos.y;
39761 //        var maxX = pos.right;
39762 //        
39763 //        var maxHeight = 0;
39764 //        
39765 //        Roo.each(items, function(item, k){
39766 //            
39767 //            var c = k % 2;
39768 //            
39769 //            item.el.position('absolute');
39770 //                
39771 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39772 //
39773 //            item.el.setWidth(width);
39774 //
39775 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39776 //
39777 //            item.el.setHeight(height);
39778 //            
39779 //            if(c == 0){
39780 //                item.el.setXY([x, y], isInstant ? false : true);
39781 //            } else {
39782 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39783 //            }
39784 //            
39785 //            y = y + height + this.alternativePadWidth;
39786 //            
39787 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39788 //            
39789 //        }, this);
39790 //        
39791 //        this.el.setHeight(maxHeight);
39792 //        
39793 //    },
39794     
39795     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39796     {
39797         var pos = this.el.getBox(true);
39798         
39799         var minX = pos.x;
39800         var minY = pos.y;
39801         
39802         var maxX = pos.right;
39803         
39804         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39805         
39806         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39807         
39808         Roo.each(queue, function(box, k){
39809             
39810             Roo.each(box, function(b, kk){
39811                 
39812                 b.el.position('absolute');
39813                 
39814                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39815                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39816                 
39817                 if(b.size == 'md-left' || b.size == 'md-right'){
39818                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39819                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39820                 }
39821                 
39822                 b.el.setWidth(width);
39823                 b.el.setHeight(height);
39824                 
39825             }, this);
39826             
39827             if(!box.length){
39828                 return;
39829             }
39830             
39831             var positions = [];
39832             
39833             switch (box.length){
39834                 case 1 :
39835                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39836                     break;
39837                 case 2 :
39838                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39839                     break;
39840                 case 3 :
39841                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39842                     break;
39843                 case 4 :
39844                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39845                     break;
39846                 default :
39847                     break;
39848             }
39849             
39850             Roo.each(box, function(b,kk){
39851                 
39852                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39853                 
39854                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39855                 
39856             }, this);
39857             
39858         }, this);
39859         
39860     },
39861     
39862     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39863     {
39864         Roo.each(eItems, function(b,k){
39865             
39866             b.size = (k == 0) ? 'sm' : 'xs';
39867             b.x = (k == 0) ? 2 : 1;
39868             b.y = (k == 0) ? 2 : 1;
39869             
39870             b.el.position('absolute');
39871             
39872             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39873                 
39874             b.el.setWidth(width);
39875             
39876             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39877             
39878             b.el.setHeight(height);
39879             
39880         }, this);
39881
39882         var positions = [];
39883         
39884         positions.push({
39885             x : maxX - this.unitWidth * 2 - this.gutter,
39886             y : minY
39887         });
39888         
39889         positions.push({
39890             x : maxX - this.unitWidth,
39891             y : minY + (this.unitWidth + this.gutter) * 2
39892         });
39893         
39894         positions.push({
39895             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39896             y : minY
39897         });
39898         
39899         Roo.each(eItems, function(b,k){
39900             
39901             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39902
39903         }, this);
39904         
39905     },
39906     
39907     getVerticalOneBoxColPositions : function(x, y, box)
39908     {
39909         var pos = [];
39910         
39911         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39912         
39913         if(box[0].size == 'md-left'){
39914             rand = 0;
39915         }
39916         
39917         if(box[0].size == 'md-right'){
39918             rand = 1;
39919         }
39920         
39921         pos.push({
39922             x : x + (this.unitWidth + this.gutter) * rand,
39923             y : y
39924         });
39925         
39926         return pos;
39927     },
39928     
39929     getVerticalTwoBoxColPositions : function(x, y, box)
39930     {
39931         var pos = [];
39932         
39933         if(box[0].size == 'xs'){
39934             
39935             pos.push({
39936                 x : x,
39937                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39938             });
39939
39940             pos.push({
39941                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39942                 y : y
39943             });
39944             
39945             return pos;
39946             
39947         }
39948         
39949         pos.push({
39950             x : x,
39951             y : y
39952         });
39953
39954         pos.push({
39955             x : x + (this.unitWidth + this.gutter) * 2,
39956             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39957         });
39958         
39959         return pos;
39960         
39961     },
39962     
39963     getVerticalThreeBoxColPositions : function(x, y, box)
39964     {
39965         var pos = [];
39966         
39967         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39968             
39969             pos.push({
39970                 x : x,
39971                 y : y
39972             });
39973
39974             pos.push({
39975                 x : x + (this.unitWidth + this.gutter) * 1,
39976                 y : y
39977             });
39978             
39979             pos.push({
39980                 x : x + (this.unitWidth + this.gutter) * 2,
39981                 y : y
39982             });
39983             
39984             return pos;
39985             
39986         }
39987         
39988         if(box[0].size == 'xs' && box[1].size == 'xs'){
39989             
39990             pos.push({
39991                 x : x,
39992                 y : y
39993             });
39994
39995             pos.push({
39996                 x : x,
39997                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39998             });
39999             
40000             pos.push({
40001                 x : x + (this.unitWidth + this.gutter) * 1,
40002                 y : y
40003             });
40004             
40005             return pos;
40006             
40007         }
40008         
40009         pos.push({
40010             x : x,
40011             y : y
40012         });
40013
40014         pos.push({
40015             x : x + (this.unitWidth + this.gutter) * 2,
40016             y : y
40017         });
40018
40019         pos.push({
40020             x : x + (this.unitWidth + this.gutter) * 2,
40021             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40022         });
40023             
40024         return pos;
40025         
40026     },
40027     
40028     getVerticalFourBoxColPositions : function(x, y, box)
40029     {
40030         var pos = [];
40031         
40032         if(box[0].size == 'xs'){
40033             
40034             pos.push({
40035                 x : x,
40036                 y : y
40037             });
40038
40039             pos.push({
40040                 x : x,
40041                 y : y + (this.unitHeight + this.gutter) * 1
40042             });
40043             
40044             pos.push({
40045                 x : x,
40046                 y : y + (this.unitHeight + this.gutter) * 2
40047             });
40048             
40049             pos.push({
40050                 x : x + (this.unitWidth + this.gutter) * 1,
40051                 y : y
40052             });
40053             
40054             return pos;
40055             
40056         }
40057         
40058         pos.push({
40059             x : x,
40060             y : y
40061         });
40062
40063         pos.push({
40064             x : x + (this.unitWidth + this.gutter) * 2,
40065             y : y
40066         });
40067
40068         pos.push({
40069             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40070             y : y + (this.unitHeight + this.gutter) * 1
40071         });
40072
40073         pos.push({
40074             x : x + (this.unitWidth + this.gutter) * 2,
40075             y : y + (this.unitWidth + this.gutter) * 2
40076         });
40077
40078         return pos;
40079         
40080     },
40081     
40082     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40083     {
40084         var pos = [];
40085         
40086         if(box[0].size == 'md-left'){
40087             pos.push({
40088                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40089                 y : minY
40090             });
40091             
40092             return pos;
40093         }
40094         
40095         if(box[0].size == 'md-right'){
40096             pos.push({
40097                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40098                 y : minY + (this.unitWidth + this.gutter) * 1
40099             });
40100             
40101             return pos;
40102         }
40103         
40104         var rand = Math.floor(Math.random() * (4 - box[0].y));
40105         
40106         pos.push({
40107             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40108             y : minY + (this.unitWidth + this.gutter) * rand
40109         });
40110         
40111         return pos;
40112         
40113     },
40114     
40115     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40116     {
40117         var pos = [];
40118         
40119         if(box[0].size == 'xs'){
40120             
40121             pos.push({
40122                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40123                 y : minY
40124             });
40125
40126             pos.push({
40127                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40128                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40129             });
40130             
40131             return pos;
40132             
40133         }
40134         
40135         pos.push({
40136             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40137             y : minY
40138         });
40139
40140         pos.push({
40141             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40142             y : minY + (this.unitWidth + this.gutter) * 2
40143         });
40144         
40145         return pos;
40146         
40147     },
40148     
40149     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40150     {
40151         var pos = [];
40152         
40153         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40154             
40155             pos.push({
40156                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40157                 y : minY
40158             });
40159
40160             pos.push({
40161                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40162                 y : minY + (this.unitWidth + this.gutter) * 1
40163             });
40164             
40165             pos.push({
40166                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40167                 y : minY + (this.unitWidth + this.gutter) * 2
40168             });
40169             
40170             return pos;
40171             
40172         }
40173         
40174         if(box[0].size == 'xs' && box[1].size == 'xs'){
40175             
40176             pos.push({
40177                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40178                 y : minY
40179             });
40180
40181             pos.push({
40182                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40183                 y : minY
40184             });
40185             
40186             pos.push({
40187                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40188                 y : minY + (this.unitWidth + this.gutter) * 1
40189             });
40190             
40191             return pos;
40192             
40193         }
40194         
40195         pos.push({
40196             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40197             y : minY
40198         });
40199
40200         pos.push({
40201             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40202             y : minY + (this.unitWidth + this.gutter) * 2
40203         });
40204
40205         pos.push({
40206             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40207             y : minY + (this.unitWidth + this.gutter) * 2
40208         });
40209             
40210         return pos;
40211         
40212     },
40213     
40214     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40215     {
40216         var pos = [];
40217         
40218         if(box[0].size == 'xs'){
40219             
40220             pos.push({
40221                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40222                 y : minY
40223             });
40224
40225             pos.push({
40226                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40227                 y : minY
40228             });
40229             
40230             pos.push({
40231                 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),
40232                 y : minY
40233             });
40234             
40235             pos.push({
40236                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40237                 y : minY + (this.unitWidth + this.gutter) * 1
40238             });
40239             
40240             return pos;
40241             
40242         }
40243         
40244         pos.push({
40245             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40246             y : minY
40247         });
40248         
40249         pos.push({
40250             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40251             y : minY + (this.unitWidth + this.gutter) * 2
40252         });
40253         
40254         pos.push({
40255             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40256             y : minY + (this.unitWidth + this.gutter) * 2
40257         });
40258         
40259         pos.push({
40260             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),
40261             y : minY + (this.unitWidth + this.gutter) * 2
40262         });
40263
40264         return pos;
40265         
40266     },
40267     
40268     /**
40269     * remove a Masonry Brick
40270     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40271     */
40272     removeBrick : function(brick_id)
40273     {
40274         if (!brick_id) {
40275             return;
40276         }
40277         
40278         for (var i = 0; i<this.bricks.length; i++) {
40279             if (this.bricks[i].id == brick_id) {
40280                 this.bricks.splice(i,1);
40281                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40282                 this.initial();
40283             }
40284         }
40285     },
40286     
40287     /**
40288     * adds a Masonry Brick
40289     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40290     */
40291     addBrick : function(cfg)
40292     {
40293         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40294         //this.register(cn);
40295         cn.parentId = this.id;
40296         cn.render(this.el);
40297         return cn;
40298     },
40299     
40300     /**
40301     * register a Masonry Brick
40302     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40303     */
40304     
40305     register : function(brick)
40306     {
40307         this.bricks.push(brick);
40308         brick.masonryId = this.id;
40309     },
40310     
40311     /**
40312     * clear all the Masonry Brick
40313     */
40314     clearAll : function()
40315     {
40316         this.bricks = [];
40317         //this.getChildContainer().dom.innerHTML = "";
40318         this.el.dom.innerHTML = '';
40319     },
40320     
40321     getSelected : function()
40322     {
40323         if (!this.selectedBrick) {
40324             return false;
40325         }
40326         
40327         return this.selectedBrick;
40328     }
40329 });
40330
40331 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40332     
40333     groups: {},
40334      /**
40335     * register a Masonry Layout
40336     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40337     */
40338     
40339     register : function(layout)
40340     {
40341         this.groups[layout.id] = layout;
40342     },
40343     /**
40344     * fetch a  Masonry Layout based on the masonry layout ID
40345     * @param {string} the masonry layout to add
40346     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40347     */
40348     
40349     get: function(layout_id) {
40350         if (typeof(this.groups[layout_id]) == 'undefined') {
40351             return false;
40352         }
40353         return this.groups[layout_id] ;
40354     }
40355     
40356     
40357     
40358 });
40359
40360  
40361
40362  /**
40363  *
40364  * This is based on 
40365  * http://masonry.desandro.com
40366  *
40367  * The idea is to render all the bricks based on vertical width...
40368  *
40369  * The original code extends 'outlayer' - we might need to use that....
40370  * 
40371  */
40372
40373
40374 /**
40375  * @class Roo.bootstrap.LayoutMasonryAuto
40376  * @extends Roo.bootstrap.Component
40377  * Bootstrap Layout Masonry class
40378  * 
40379  * @constructor
40380  * Create a new Element
40381  * @param {Object} config The config object
40382  */
40383
40384 Roo.bootstrap.LayoutMasonryAuto = function(config){
40385     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40386 };
40387
40388 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40389     
40390       /**
40391      * @cfg {Boolean} isFitWidth  - resize the width..
40392      */   
40393     isFitWidth : false,  // options..
40394     /**
40395      * @cfg {Boolean} isOriginLeft = left align?
40396      */   
40397     isOriginLeft : true,
40398     /**
40399      * @cfg {Boolean} isOriginTop = top align?
40400      */   
40401     isOriginTop : false,
40402     /**
40403      * @cfg {Boolean} isLayoutInstant = no animation?
40404      */   
40405     isLayoutInstant : false, // needed?
40406     /**
40407      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40408      */   
40409     isResizingContainer : true,
40410     /**
40411      * @cfg {Number} columnWidth  width of the columns 
40412      */   
40413     
40414     columnWidth : 0,
40415     
40416     /**
40417      * @cfg {Number} maxCols maximum number of columns
40418      */   
40419     
40420     maxCols: 0,
40421     /**
40422      * @cfg {Number} padHeight padding below box..
40423      */   
40424     
40425     padHeight : 10, 
40426     
40427     /**
40428      * @cfg {Boolean} isAutoInitial defalut true
40429      */   
40430     
40431     isAutoInitial : true, 
40432     
40433     // private?
40434     gutter : 0,
40435     
40436     containerWidth: 0,
40437     initialColumnWidth : 0,
40438     currentSize : null,
40439     
40440     colYs : null, // array.
40441     maxY : 0,
40442     padWidth: 10,
40443     
40444     
40445     tag: 'div',
40446     cls: '',
40447     bricks: null, //CompositeElement
40448     cols : 0, // array?
40449     // element : null, // wrapped now this.el
40450     _isLayoutInited : null, 
40451     
40452     
40453     getAutoCreate : function(){
40454         
40455         var cfg = {
40456             tag: this.tag,
40457             cls: 'blog-masonary-wrapper ' + this.cls,
40458             cn : {
40459                 cls : 'mas-boxes masonary'
40460             }
40461         };
40462         
40463         return cfg;
40464     },
40465     
40466     getChildContainer: function( )
40467     {
40468         if (this.boxesEl) {
40469             return this.boxesEl;
40470         }
40471         
40472         this.boxesEl = this.el.select('.mas-boxes').first();
40473         
40474         return this.boxesEl;
40475     },
40476     
40477     
40478     initEvents : function()
40479     {
40480         var _this = this;
40481         
40482         if(this.isAutoInitial){
40483             Roo.log('hook children rendered');
40484             this.on('childrenrendered', function() {
40485                 Roo.log('children rendered');
40486                 _this.initial();
40487             } ,this);
40488         }
40489         
40490     },
40491     
40492     initial : function()
40493     {
40494         this.reloadItems();
40495
40496         this.currentSize = this.el.getBox(true);
40497
40498         /// was window resize... - let's see if this works..
40499         Roo.EventManager.onWindowResize(this.resize, this); 
40500
40501         if(!this.isAutoInitial){
40502             this.layout();
40503             return;
40504         }
40505         
40506         this.layout.defer(500,this);
40507     },
40508     
40509     reloadItems: function()
40510     {
40511         this.bricks = this.el.select('.masonry-brick', true);
40512         
40513         this.bricks.each(function(b) {
40514             //Roo.log(b.getSize());
40515             if (!b.attr('originalwidth')) {
40516                 b.attr('originalwidth',  b.getSize().width);
40517             }
40518             
40519         });
40520         
40521         Roo.log(this.bricks.elements.length);
40522     },
40523     
40524     resize : function()
40525     {
40526         Roo.log('resize');
40527         var cs = this.el.getBox(true);
40528         
40529         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40530             Roo.log("no change in with or X");
40531             return;
40532         }
40533         this.currentSize = cs;
40534         this.layout();
40535     },
40536     
40537     layout : function()
40538     {
40539          Roo.log('layout');
40540         this._resetLayout();
40541         //this._manageStamps();
40542       
40543         // don't animate first layout
40544         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40545         this.layoutItems( isInstant );
40546       
40547         // flag for initalized
40548         this._isLayoutInited = true;
40549     },
40550     
40551     layoutItems : function( isInstant )
40552     {
40553         //var items = this._getItemsForLayout( this.items );
40554         // original code supports filtering layout items.. we just ignore it..
40555         
40556         this._layoutItems( this.bricks , isInstant );
40557       
40558         this._postLayout();
40559     },
40560     _layoutItems : function ( items , isInstant)
40561     {
40562        //this.fireEvent( 'layout', this, items );
40563     
40564
40565         if ( !items || !items.elements.length ) {
40566           // no items, emit event with empty array
40567             return;
40568         }
40569
40570         var queue = [];
40571         items.each(function(item) {
40572             Roo.log("layout item");
40573             Roo.log(item);
40574             // get x/y object from method
40575             var position = this._getItemLayoutPosition( item );
40576             // enqueue
40577             position.item = item;
40578             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40579             queue.push( position );
40580         }, this);
40581       
40582         this._processLayoutQueue( queue );
40583     },
40584     /** Sets position of item in DOM
40585     * @param {Element} item
40586     * @param {Number} x - horizontal position
40587     * @param {Number} y - vertical position
40588     * @param {Boolean} isInstant - disables transitions
40589     */
40590     _processLayoutQueue : function( queue )
40591     {
40592         for ( var i=0, len = queue.length; i < len; i++ ) {
40593             var obj = queue[i];
40594             obj.item.position('absolute');
40595             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40596         }
40597     },
40598       
40599     
40600     /**
40601     * Any logic you want to do after each layout,
40602     * i.e. size the container
40603     */
40604     _postLayout : function()
40605     {
40606         this.resizeContainer();
40607     },
40608     
40609     resizeContainer : function()
40610     {
40611         if ( !this.isResizingContainer ) {
40612             return;
40613         }
40614         var size = this._getContainerSize();
40615         if ( size ) {
40616             this.el.setSize(size.width,size.height);
40617             this.boxesEl.setSize(size.width,size.height);
40618         }
40619     },
40620     
40621     
40622     
40623     _resetLayout : function()
40624     {
40625         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40626         this.colWidth = this.el.getWidth();
40627         //this.gutter = this.el.getWidth(); 
40628         
40629         this.measureColumns();
40630
40631         // reset column Y
40632         var i = this.cols;
40633         this.colYs = [];
40634         while (i--) {
40635             this.colYs.push( 0 );
40636         }
40637     
40638         this.maxY = 0;
40639     },
40640
40641     measureColumns : function()
40642     {
40643         this.getContainerWidth();
40644       // if columnWidth is 0, default to outerWidth of first item
40645         if ( !this.columnWidth ) {
40646             var firstItem = this.bricks.first();
40647             Roo.log(firstItem);
40648             this.columnWidth  = this.containerWidth;
40649             if (firstItem && firstItem.attr('originalwidth') ) {
40650                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40651             }
40652             // columnWidth fall back to item of first element
40653             Roo.log("set column width?");
40654                         this.initialColumnWidth = this.columnWidth  ;
40655
40656             // if first elem has no width, default to size of container
40657             
40658         }
40659         
40660         
40661         if (this.initialColumnWidth) {
40662             this.columnWidth = this.initialColumnWidth;
40663         }
40664         
40665         
40666             
40667         // column width is fixed at the top - however if container width get's smaller we should
40668         // reduce it...
40669         
40670         // this bit calcs how man columns..
40671             
40672         var columnWidth = this.columnWidth += this.gutter;
40673       
40674         // calculate columns
40675         var containerWidth = this.containerWidth + this.gutter;
40676         
40677         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40678         // fix rounding errors, typically with gutters
40679         var excess = columnWidth - containerWidth % columnWidth;
40680         
40681         
40682         // if overshoot is less than a pixel, round up, otherwise floor it
40683         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40684         cols = Math[ mathMethod ]( cols );
40685         this.cols = Math.max( cols, 1 );
40686         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40687         
40688          // padding positioning..
40689         var totalColWidth = this.cols * this.columnWidth;
40690         var padavail = this.containerWidth - totalColWidth;
40691         // so for 2 columns - we need 3 'pads'
40692         
40693         var padNeeded = (1+this.cols) * this.padWidth;
40694         
40695         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40696         
40697         this.columnWidth += padExtra
40698         //this.padWidth = Math.floor(padavail /  ( this.cols));
40699         
40700         // adjust colum width so that padding is fixed??
40701         
40702         // we have 3 columns ... total = width * 3
40703         // we have X left over... that should be used by 
40704         
40705         //if (this.expandC) {
40706             
40707         //}
40708         
40709         
40710         
40711     },
40712     
40713     getContainerWidth : function()
40714     {
40715        /* // container is parent if fit width
40716         var container = this.isFitWidth ? this.element.parentNode : this.element;
40717         // check that this.size and size are there
40718         // IE8 triggers resize on body size change, so they might not be
40719         
40720         var size = getSize( container );  //FIXME
40721         this.containerWidth = size && size.innerWidth; //FIXME
40722         */
40723          
40724         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40725         
40726     },
40727     
40728     _getItemLayoutPosition : function( item )  // what is item?
40729     {
40730         // we resize the item to our columnWidth..
40731       
40732         item.setWidth(this.columnWidth);
40733         item.autoBoxAdjust  = false;
40734         
40735         var sz = item.getSize();
40736  
40737         // how many columns does this brick span
40738         var remainder = this.containerWidth % this.columnWidth;
40739         
40740         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40741         // round if off by 1 pixel, otherwise use ceil
40742         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40743         colSpan = Math.min( colSpan, this.cols );
40744         
40745         // normally this should be '1' as we dont' currently allow multi width columns..
40746         
40747         var colGroup = this._getColGroup( colSpan );
40748         // get the minimum Y value from the columns
40749         var minimumY = Math.min.apply( Math, colGroup );
40750         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40751         
40752         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40753          
40754         // position the brick
40755         var position = {
40756             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40757             y: this.currentSize.y + minimumY + this.padHeight
40758         };
40759         
40760         Roo.log(position);
40761         // apply setHeight to necessary columns
40762         var setHeight = minimumY + sz.height + this.padHeight;
40763         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40764         
40765         var setSpan = this.cols + 1 - colGroup.length;
40766         for ( var i = 0; i < setSpan; i++ ) {
40767           this.colYs[ shortColIndex + i ] = setHeight ;
40768         }
40769       
40770         return position;
40771     },
40772     
40773     /**
40774      * @param {Number} colSpan - number of columns the element spans
40775      * @returns {Array} colGroup
40776      */
40777     _getColGroup : function( colSpan )
40778     {
40779         if ( colSpan < 2 ) {
40780           // if brick spans only one column, use all the column Ys
40781           return this.colYs;
40782         }
40783       
40784         var colGroup = [];
40785         // how many different places could this brick fit horizontally
40786         var groupCount = this.cols + 1 - colSpan;
40787         // for each group potential horizontal position
40788         for ( var i = 0; i < groupCount; i++ ) {
40789           // make an array of colY values for that one group
40790           var groupColYs = this.colYs.slice( i, i + colSpan );
40791           // and get the max value of the array
40792           colGroup[i] = Math.max.apply( Math, groupColYs );
40793         }
40794         return colGroup;
40795     },
40796     /*
40797     _manageStamp : function( stamp )
40798     {
40799         var stampSize =  stamp.getSize();
40800         var offset = stamp.getBox();
40801         // get the columns that this stamp affects
40802         var firstX = this.isOriginLeft ? offset.x : offset.right;
40803         var lastX = firstX + stampSize.width;
40804         var firstCol = Math.floor( firstX / this.columnWidth );
40805         firstCol = Math.max( 0, firstCol );
40806         
40807         var lastCol = Math.floor( lastX / this.columnWidth );
40808         // lastCol should not go over if multiple of columnWidth #425
40809         lastCol -= lastX % this.columnWidth ? 0 : 1;
40810         lastCol = Math.min( this.cols - 1, lastCol );
40811         
40812         // set colYs to bottom of the stamp
40813         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40814             stampSize.height;
40815             
40816         for ( var i = firstCol; i <= lastCol; i++ ) {
40817           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40818         }
40819     },
40820     */
40821     
40822     _getContainerSize : function()
40823     {
40824         this.maxY = Math.max.apply( Math, this.colYs );
40825         var size = {
40826             height: this.maxY
40827         };
40828       
40829         if ( this.isFitWidth ) {
40830             size.width = this._getContainerFitWidth();
40831         }
40832       
40833         return size;
40834     },
40835     
40836     _getContainerFitWidth : function()
40837     {
40838         var unusedCols = 0;
40839         // count unused columns
40840         var i = this.cols;
40841         while ( --i ) {
40842           if ( this.colYs[i] !== 0 ) {
40843             break;
40844           }
40845           unusedCols++;
40846         }
40847         // fit container to columns that have been used
40848         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40849     },
40850     
40851     needsResizeLayout : function()
40852     {
40853         var previousWidth = this.containerWidth;
40854         this.getContainerWidth();
40855         return previousWidth !== this.containerWidth;
40856     }
40857  
40858 });
40859
40860  
40861
40862  /*
40863  * - LGPL
40864  *
40865  * element
40866  * 
40867  */
40868
40869 /**
40870  * @class Roo.bootstrap.MasonryBrick
40871  * @extends Roo.bootstrap.Component
40872  * Bootstrap MasonryBrick class
40873  * 
40874  * @constructor
40875  * Create a new MasonryBrick
40876  * @param {Object} config The config object
40877  */
40878
40879 Roo.bootstrap.MasonryBrick = function(config){
40880     
40881     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40882     
40883     Roo.bootstrap.MasonryBrick.register(this);
40884     
40885     this.addEvents({
40886         // raw events
40887         /**
40888          * @event click
40889          * When a MasonryBrick is clcik
40890          * @param {Roo.bootstrap.MasonryBrick} this
40891          * @param {Roo.EventObject} e
40892          */
40893         "click" : true
40894     });
40895 };
40896
40897 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40898     
40899     /**
40900      * @cfg {String} title
40901      */   
40902     title : '',
40903     /**
40904      * @cfg {String} html
40905      */   
40906     html : '',
40907     /**
40908      * @cfg {String} bgimage
40909      */   
40910     bgimage : '',
40911     /**
40912      * @cfg {String} videourl
40913      */   
40914     videourl : '',
40915     /**
40916      * @cfg {String} cls
40917      */   
40918     cls : '',
40919     /**
40920      * @cfg {String} href
40921      */   
40922     href : '',
40923     /**
40924      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40925      */   
40926     size : 'xs',
40927     
40928     /**
40929      * @cfg {String} placetitle (center|bottom)
40930      */   
40931     placetitle : '',
40932     
40933     /**
40934      * @cfg {Boolean} isFitContainer defalut true
40935      */   
40936     isFitContainer : true, 
40937     
40938     /**
40939      * @cfg {Boolean} preventDefault defalut false
40940      */   
40941     preventDefault : false, 
40942     
40943     /**
40944      * @cfg {Boolean} inverse defalut false
40945      */   
40946     maskInverse : false, 
40947     
40948     getAutoCreate : function()
40949     {
40950         if(!this.isFitContainer){
40951             return this.getSplitAutoCreate();
40952         }
40953         
40954         var cls = 'masonry-brick masonry-brick-full';
40955         
40956         if(this.href.length){
40957             cls += ' masonry-brick-link';
40958         }
40959         
40960         if(this.bgimage.length){
40961             cls += ' masonry-brick-image';
40962         }
40963         
40964         if(this.maskInverse){
40965             cls += ' mask-inverse';
40966         }
40967         
40968         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40969             cls += ' enable-mask';
40970         }
40971         
40972         if(this.size){
40973             cls += ' masonry-' + this.size + '-brick';
40974         }
40975         
40976         if(this.placetitle.length){
40977             
40978             switch (this.placetitle) {
40979                 case 'center' :
40980                     cls += ' masonry-center-title';
40981                     break;
40982                 case 'bottom' :
40983                     cls += ' masonry-bottom-title';
40984                     break;
40985                 default:
40986                     break;
40987             }
40988             
40989         } else {
40990             if(!this.html.length && !this.bgimage.length){
40991                 cls += ' masonry-center-title';
40992             }
40993
40994             if(!this.html.length && this.bgimage.length){
40995                 cls += ' masonry-bottom-title';
40996             }
40997         }
40998         
40999         if(this.cls){
41000             cls += ' ' + this.cls;
41001         }
41002         
41003         var cfg = {
41004             tag: (this.href.length) ? 'a' : 'div',
41005             cls: cls,
41006             cn: [
41007                 {
41008                     tag: 'div',
41009                     cls: 'masonry-brick-mask'
41010                 },
41011                 {
41012                     tag: 'div',
41013                     cls: 'masonry-brick-paragraph',
41014                     cn: []
41015                 }
41016             ]
41017         };
41018         
41019         if(this.href.length){
41020             cfg.href = this.href;
41021         }
41022         
41023         var cn = cfg.cn[1].cn;
41024         
41025         if(this.title.length){
41026             cn.push({
41027                 tag: 'h4',
41028                 cls: 'masonry-brick-title',
41029                 html: this.title
41030             });
41031         }
41032         
41033         if(this.html.length){
41034             cn.push({
41035                 tag: 'p',
41036                 cls: 'masonry-brick-text',
41037                 html: this.html
41038             });
41039         }
41040         
41041         if (!this.title.length && !this.html.length) {
41042             cfg.cn[1].cls += ' hide';
41043         }
41044         
41045         if(this.bgimage.length){
41046             cfg.cn.push({
41047                 tag: 'img',
41048                 cls: 'masonry-brick-image-view',
41049                 src: this.bgimage
41050             });
41051         }
41052         
41053         if(this.videourl.length){
41054             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41055             // youtube support only?
41056             cfg.cn.push({
41057                 tag: 'iframe',
41058                 cls: 'masonry-brick-image-view',
41059                 src: vurl,
41060                 frameborder : 0,
41061                 allowfullscreen : true
41062             });
41063         }
41064         
41065         return cfg;
41066         
41067     },
41068     
41069     getSplitAutoCreate : function()
41070     {
41071         var cls = 'masonry-brick masonry-brick-split';
41072         
41073         if(this.href.length){
41074             cls += ' masonry-brick-link';
41075         }
41076         
41077         if(this.bgimage.length){
41078             cls += ' masonry-brick-image';
41079         }
41080         
41081         if(this.size){
41082             cls += ' masonry-' + this.size + '-brick';
41083         }
41084         
41085         switch (this.placetitle) {
41086             case 'center' :
41087                 cls += ' masonry-center-title';
41088                 break;
41089             case 'bottom' :
41090                 cls += ' masonry-bottom-title';
41091                 break;
41092             default:
41093                 if(!this.bgimage.length){
41094                     cls += ' masonry-center-title';
41095                 }
41096
41097                 if(this.bgimage.length){
41098                     cls += ' masonry-bottom-title';
41099                 }
41100                 break;
41101         }
41102         
41103         if(this.cls){
41104             cls += ' ' + this.cls;
41105         }
41106         
41107         var cfg = {
41108             tag: (this.href.length) ? 'a' : 'div',
41109             cls: cls,
41110             cn: [
41111                 {
41112                     tag: 'div',
41113                     cls: 'masonry-brick-split-head',
41114                     cn: [
41115                         {
41116                             tag: 'div',
41117                             cls: 'masonry-brick-paragraph',
41118                             cn: []
41119                         }
41120                     ]
41121                 },
41122                 {
41123                     tag: 'div',
41124                     cls: 'masonry-brick-split-body',
41125                     cn: []
41126                 }
41127             ]
41128         };
41129         
41130         if(this.href.length){
41131             cfg.href = this.href;
41132         }
41133         
41134         if(this.title.length){
41135             cfg.cn[0].cn[0].cn.push({
41136                 tag: 'h4',
41137                 cls: 'masonry-brick-title',
41138                 html: this.title
41139             });
41140         }
41141         
41142         if(this.html.length){
41143             cfg.cn[1].cn.push({
41144                 tag: 'p',
41145                 cls: 'masonry-brick-text',
41146                 html: this.html
41147             });
41148         }
41149
41150         if(this.bgimage.length){
41151             cfg.cn[0].cn.push({
41152                 tag: 'img',
41153                 cls: 'masonry-brick-image-view',
41154                 src: this.bgimage
41155             });
41156         }
41157         
41158         if(this.videourl.length){
41159             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41160             // youtube support only?
41161             cfg.cn[0].cn.cn.push({
41162                 tag: 'iframe',
41163                 cls: 'masonry-brick-image-view',
41164                 src: vurl,
41165                 frameborder : 0,
41166                 allowfullscreen : true
41167             });
41168         }
41169         
41170         return cfg;
41171     },
41172     
41173     initEvents: function() 
41174     {
41175         switch (this.size) {
41176             case 'xs' :
41177                 this.x = 1;
41178                 this.y = 1;
41179                 break;
41180             case 'sm' :
41181                 this.x = 2;
41182                 this.y = 2;
41183                 break;
41184             case 'md' :
41185             case 'md-left' :
41186             case 'md-right' :
41187                 this.x = 3;
41188                 this.y = 3;
41189                 break;
41190             case 'tall' :
41191                 this.x = 2;
41192                 this.y = 3;
41193                 break;
41194             case 'wide' :
41195                 this.x = 3;
41196                 this.y = 2;
41197                 break;
41198             case 'wide-thin' :
41199                 this.x = 3;
41200                 this.y = 1;
41201                 break;
41202                         
41203             default :
41204                 break;
41205         }
41206         
41207         if(Roo.isTouch){
41208             this.el.on('touchstart', this.onTouchStart, this);
41209             this.el.on('touchmove', this.onTouchMove, this);
41210             this.el.on('touchend', this.onTouchEnd, this);
41211             this.el.on('contextmenu', this.onContextMenu, this);
41212         } else {
41213             this.el.on('mouseenter'  ,this.enter, this);
41214             this.el.on('mouseleave', this.leave, this);
41215             this.el.on('click', this.onClick, this);
41216         }
41217         
41218         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41219             this.parent().bricks.push(this);   
41220         }
41221         
41222     },
41223     
41224     onClick: function(e, el)
41225     {
41226         var time = this.endTimer - this.startTimer;
41227         // Roo.log(e.preventDefault());
41228         if(Roo.isTouch){
41229             if(time > 1000){
41230                 e.preventDefault();
41231                 return;
41232             }
41233         }
41234         
41235         if(!this.preventDefault){
41236             return;
41237         }
41238         
41239         e.preventDefault();
41240         
41241         if (this.activeClass != '') {
41242             this.selectBrick();
41243         }
41244         
41245         this.fireEvent('click', this, e);
41246     },
41247     
41248     enter: function(e, el)
41249     {
41250         e.preventDefault();
41251         
41252         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41253             return;
41254         }
41255         
41256         if(this.bgimage.length && this.html.length){
41257             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41258         }
41259     },
41260     
41261     leave: function(e, el)
41262     {
41263         e.preventDefault();
41264         
41265         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41266             return;
41267         }
41268         
41269         if(this.bgimage.length && this.html.length){
41270             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41271         }
41272     },
41273     
41274     onTouchStart: function(e, el)
41275     {
41276 //        e.preventDefault();
41277         
41278         this.touchmoved = false;
41279         
41280         if(!this.isFitContainer){
41281             return;
41282         }
41283         
41284         if(!this.bgimage.length || !this.html.length){
41285             return;
41286         }
41287         
41288         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41289         
41290         this.timer = new Date().getTime();
41291         
41292     },
41293     
41294     onTouchMove: function(e, el)
41295     {
41296         this.touchmoved = true;
41297     },
41298     
41299     onContextMenu : function(e,el)
41300     {
41301         e.preventDefault();
41302         e.stopPropagation();
41303         return false;
41304     },
41305     
41306     onTouchEnd: function(e, el)
41307     {
41308 //        e.preventDefault();
41309         
41310         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41311         
41312             this.leave(e,el);
41313             
41314             return;
41315         }
41316         
41317         if(!this.bgimage.length || !this.html.length){
41318             
41319             if(this.href.length){
41320                 window.location.href = this.href;
41321             }
41322             
41323             return;
41324         }
41325         
41326         if(!this.isFitContainer){
41327             return;
41328         }
41329         
41330         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41331         
41332         window.location.href = this.href;
41333     },
41334     
41335     //selection on single brick only
41336     selectBrick : function() {
41337         
41338         if (!this.parentId) {
41339             return;
41340         }
41341         
41342         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41343         var index = m.selectedBrick.indexOf(this.id);
41344         
41345         if ( index > -1) {
41346             m.selectedBrick.splice(index,1);
41347             this.el.removeClass(this.activeClass);
41348             return;
41349         }
41350         
41351         for(var i = 0; i < m.selectedBrick.length; i++) {
41352             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41353             b.el.removeClass(b.activeClass);
41354         }
41355         
41356         m.selectedBrick = [];
41357         
41358         m.selectedBrick.push(this.id);
41359         this.el.addClass(this.activeClass);
41360         return;
41361     },
41362     
41363     isSelected : function(){
41364         return this.el.hasClass(this.activeClass);
41365         
41366     }
41367 });
41368
41369 Roo.apply(Roo.bootstrap.MasonryBrick, {
41370     
41371     //groups: {},
41372     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41373      /**
41374     * register a Masonry Brick
41375     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41376     */
41377     
41378     register : function(brick)
41379     {
41380         //this.groups[brick.id] = brick;
41381         this.groups.add(brick.id, brick);
41382     },
41383     /**
41384     * fetch a  masonry brick based on the masonry brick ID
41385     * @param {string} the masonry brick to add
41386     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41387     */
41388     
41389     get: function(brick_id) 
41390     {
41391         // if (typeof(this.groups[brick_id]) == 'undefined') {
41392         //     return false;
41393         // }
41394         // return this.groups[brick_id] ;
41395         
41396         if(this.groups.key(brick_id)) {
41397             return this.groups.key(brick_id);
41398         }
41399         
41400         return false;
41401     }
41402     
41403     
41404     
41405 });
41406
41407  /*
41408  * - LGPL
41409  *
41410  * element
41411  * 
41412  */
41413
41414 /**
41415  * @class Roo.bootstrap.Brick
41416  * @extends Roo.bootstrap.Component
41417  * Bootstrap Brick class
41418  * 
41419  * @constructor
41420  * Create a new Brick
41421  * @param {Object} config The config object
41422  */
41423
41424 Roo.bootstrap.Brick = function(config){
41425     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41426     
41427     this.addEvents({
41428         // raw events
41429         /**
41430          * @event click
41431          * When a Brick is click
41432          * @param {Roo.bootstrap.Brick} this
41433          * @param {Roo.EventObject} e
41434          */
41435         "click" : true
41436     });
41437 };
41438
41439 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41440     
41441     /**
41442      * @cfg {String} title
41443      */   
41444     title : '',
41445     /**
41446      * @cfg {String} html
41447      */   
41448     html : '',
41449     /**
41450      * @cfg {String} bgimage
41451      */   
41452     bgimage : '',
41453     /**
41454      * @cfg {String} cls
41455      */   
41456     cls : '',
41457     /**
41458      * @cfg {String} href
41459      */   
41460     href : '',
41461     /**
41462      * @cfg {String} video
41463      */   
41464     video : '',
41465     /**
41466      * @cfg {Boolean} square
41467      */   
41468     square : true,
41469     
41470     getAutoCreate : function()
41471     {
41472         var cls = 'roo-brick';
41473         
41474         if(this.href.length){
41475             cls += ' roo-brick-link';
41476         }
41477         
41478         if(this.bgimage.length){
41479             cls += ' roo-brick-image';
41480         }
41481         
41482         if(!this.html.length && !this.bgimage.length){
41483             cls += ' roo-brick-center-title';
41484         }
41485         
41486         if(!this.html.length && this.bgimage.length){
41487             cls += ' roo-brick-bottom-title';
41488         }
41489         
41490         if(this.cls){
41491             cls += ' ' + this.cls;
41492         }
41493         
41494         var cfg = {
41495             tag: (this.href.length) ? 'a' : 'div',
41496             cls: cls,
41497             cn: [
41498                 {
41499                     tag: 'div',
41500                     cls: 'roo-brick-paragraph',
41501                     cn: []
41502                 }
41503             ]
41504         };
41505         
41506         if(this.href.length){
41507             cfg.href = this.href;
41508         }
41509         
41510         var cn = cfg.cn[0].cn;
41511         
41512         if(this.title.length){
41513             cn.push({
41514                 tag: 'h4',
41515                 cls: 'roo-brick-title',
41516                 html: this.title
41517             });
41518         }
41519         
41520         if(this.html.length){
41521             cn.push({
41522                 tag: 'p',
41523                 cls: 'roo-brick-text',
41524                 html: this.html
41525             });
41526         } else {
41527             cn.cls += ' hide';
41528         }
41529         
41530         if(this.bgimage.length){
41531             cfg.cn.push({
41532                 tag: 'img',
41533                 cls: 'roo-brick-image-view',
41534                 src: this.bgimage
41535             });
41536         }
41537         
41538         return cfg;
41539     },
41540     
41541     initEvents: function() 
41542     {
41543         if(this.title.length || this.html.length){
41544             this.el.on('mouseenter'  ,this.enter, this);
41545             this.el.on('mouseleave', this.leave, this);
41546         }
41547         
41548         Roo.EventManager.onWindowResize(this.resize, this); 
41549         
41550         if(this.bgimage.length){
41551             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41552             this.imageEl.on('load', this.onImageLoad, this);
41553             return;
41554         }
41555         
41556         this.resize();
41557     },
41558     
41559     onImageLoad : function()
41560     {
41561         this.resize();
41562     },
41563     
41564     resize : function()
41565     {
41566         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41567         
41568         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41569         
41570         if(this.bgimage.length){
41571             var image = this.el.select('.roo-brick-image-view', true).first();
41572             
41573             image.setWidth(paragraph.getWidth());
41574             
41575             if(this.square){
41576                 image.setHeight(paragraph.getWidth());
41577             }
41578             
41579             this.el.setHeight(image.getHeight());
41580             paragraph.setHeight(image.getHeight());
41581             
41582         }
41583         
41584     },
41585     
41586     enter: function(e, el)
41587     {
41588         e.preventDefault();
41589         
41590         if(this.bgimage.length){
41591             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41592             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41593         }
41594     },
41595     
41596     leave: function(e, el)
41597     {
41598         e.preventDefault();
41599         
41600         if(this.bgimage.length){
41601             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41602             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41603         }
41604     }
41605     
41606 });
41607
41608  
41609
41610  /*
41611  * - LGPL
41612  *
41613  * Number field 
41614  */
41615
41616 /**
41617  * @class Roo.bootstrap.form.NumberField
41618  * @extends Roo.bootstrap.form.Input
41619  * Bootstrap NumberField class
41620  * 
41621  * 
41622  * 
41623  * 
41624  * @constructor
41625  * Create a new NumberField
41626  * @param {Object} config The config object
41627  */
41628
41629 Roo.bootstrap.form.NumberField = function(config){
41630     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41631 };
41632
41633 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41634     
41635     /**
41636      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41637      */
41638     allowDecimals : true,
41639     /**
41640      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41641      */
41642     decimalSeparator : ".",
41643     /**
41644      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41645      */
41646     decimalPrecision : 2,
41647     /**
41648      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41649      */
41650     allowNegative : true,
41651     
41652     /**
41653      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41654      */
41655     allowZero: true,
41656     /**
41657      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41658      */
41659     minValue : Number.NEGATIVE_INFINITY,
41660     /**
41661      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41662      */
41663     maxValue : Number.MAX_VALUE,
41664     /**
41665      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41666      */
41667     minText : "The minimum value for this field is {0}",
41668     /**
41669      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41670      */
41671     maxText : "The maximum value for this field is {0}",
41672     /**
41673      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41674      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41675      */
41676     nanText : "{0} is not a valid number",
41677     /**
41678      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41679      */
41680     thousandsDelimiter : false,
41681     /**
41682      * @cfg {String} valueAlign alignment of value
41683      */
41684     valueAlign : "left",
41685
41686     getAutoCreate : function()
41687     {
41688         var hiddenInput = {
41689             tag: 'input',
41690             type: 'hidden',
41691             id: Roo.id(),
41692             cls: 'hidden-number-input'
41693         };
41694         
41695         if (this.name) {
41696             hiddenInput.name = this.name;
41697         }
41698         
41699         this.name = '';
41700         
41701         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41702         
41703         this.name = hiddenInput.name;
41704         
41705         if(cfg.cn.length > 0) {
41706             cfg.cn.push(hiddenInput);
41707         }
41708         
41709         return cfg;
41710     },
41711
41712     // private
41713     initEvents : function()
41714     {   
41715         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41716         
41717         var allowed = "0123456789";
41718         
41719         if(this.allowDecimals){
41720             allowed += this.decimalSeparator;
41721         }
41722         
41723         if(this.allowNegative){
41724             allowed += "-";
41725         }
41726         
41727         if(this.thousandsDelimiter) {
41728             allowed += ",";
41729         }
41730         
41731         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41732         
41733         var keyPress = function(e){
41734             
41735             var k = e.getKey();
41736             
41737             var c = e.getCharCode();
41738             
41739             if(
41740                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41741                     allowed.indexOf(String.fromCharCode(c)) === -1
41742             ){
41743                 e.stopEvent();
41744                 return;
41745             }
41746             
41747             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41748                 return;
41749             }
41750             
41751             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41752                 e.stopEvent();
41753             }
41754         };
41755         
41756         this.el.on("keypress", keyPress, this);
41757     },
41758     
41759     validateValue : function(value)
41760     {
41761         
41762         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41763             return false;
41764         }
41765         
41766         var num = this.parseValue(value);
41767         
41768         if(isNaN(num)){
41769             this.markInvalid(String.format(this.nanText, value));
41770             return false;
41771         }
41772         
41773         if(num < this.minValue){
41774             this.markInvalid(String.format(this.minText, this.minValue));
41775             return false;
41776         }
41777         
41778         if(num > this.maxValue){
41779             this.markInvalid(String.format(this.maxText, this.maxValue));
41780             return false;
41781         }
41782         
41783         return true;
41784     },
41785
41786     getValue : function()
41787     {
41788         var v = this.hiddenEl().getValue();
41789         
41790         return this.fixPrecision(this.parseValue(v));
41791     },
41792
41793     parseValue : function(value)
41794     {
41795         if(this.thousandsDelimiter) {
41796             value += "";
41797             r = new RegExp(",", "g");
41798             value = value.replace(r, "");
41799         }
41800         
41801         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41802         return isNaN(value) ? '' : value;
41803     },
41804
41805     fixPrecision : function(value)
41806     {
41807         if(this.thousandsDelimiter) {
41808             value += "";
41809             r = new RegExp(",", "g");
41810             value = value.replace(r, "");
41811         }
41812         
41813         var nan = isNaN(value);
41814         
41815         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41816             return nan ? '' : value;
41817         }
41818         return parseFloat(value).toFixed(this.decimalPrecision);
41819     },
41820
41821     setValue : function(v)
41822     {
41823         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41824         
41825         this.value = v;
41826         
41827         if(this.rendered){
41828             
41829             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41830             
41831             this.inputEl().dom.value = (v == '') ? '' :
41832                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41833             
41834             if(!this.allowZero && v === '0') {
41835                 this.hiddenEl().dom.value = '';
41836                 this.inputEl().dom.value = '';
41837             }
41838             
41839             this.validate();
41840         }
41841     },
41842
41843     decimalPrecisionFcn : function(v)
41844     {
41845         return Math.floor(v);
41846     },
41847
41848     beforeBlur : function()
41849     {
41850         var v = this.parseValue(this.getRawValue());
41851         
41852         if(v || v === 0 || v === ''){
41853             this.setValue(v);
41854         }
41855     },
41856     
41857     hiddenEl : function()
41858     {
41859         return this.el.select('input.hidden-number-input',true).first();
41860     }
41861     
41862 });
41863
41864  
41865
41866 /*
41867 * Licence: LGPL
41868 */
41869
41870 /**
41871  * @class Roo.bootstrap.DocumentSlider
41872  * @extends Roo.bootstrap.Component
41873  * Bootstrap DocumentSlider class
41874  * 
41875  * @constructor
41876  * Create a new DocumentViewer
41877  * @param {Object} config The config object
41878  */
41879
41880 Roo.bootstrap.DocumentSlider = function(config){
41881     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41882     
41883     this.files = [];
41884     
41885     this.addEvents({
41886         /**
41887          * @event initial
41888          * Fire after initEvent
41889          * @param {Roo.bootstrap.DocumentSlider} this
41890          */
41891         "initial" : true,
41892         /**
41893          * @event update
41894          * Fire after update
41895          * @param {Roo.bootstrap.DocumentSlider} this
41896          */
41897         "update" : true,
41898         /**
41899          * @event click
41900          * Fire after click
41901          * @param {Roo.bootstrap.DocumentSlider} this
41902          */
41903         "click" : true
41904     });
41905 };
41906
41907 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41908     
41909     files : false,
41910     
41911     indicator : 0,
41912     
41913     getAutoCreate : function()
41914     {
41915         var cfg = {
41916             tag : 'div',
41917             cls : 'roo-document-slider',
41918             cn : [
41919                 {
41920                     tag : 'div',
41921                     cls : 'roo-document-slider-header',
41922                     cn : [
41923                         {
41924                             tag : 'div',
41925                             cls : 'roo-document-slider-header-title'
41926                         }
41927                     ]
41928                 },
41929                 {
41930                     tag : 'div',
41931                     cls : 'roo-document-slider-body',
41932                     cn : [
41933                         {
41934                             tag : 'div',
41935                             cls : 'roo-document-slider-prev',
41936                             cn : [
41937                                 {
41938                                     tag : 'i',
41939                                     cls : 'fa fa-chevron-left'
41940                                 }
41941                             ]
41942                         },
41943                         {
41944                             tag : 'div',
41945                             cls : 'roo-document-slider-thumb',
41946                             cn : [
41947                                 {
41948                                     tag : 'img',
41949                                     cls : 'roo-document-slider-image'
41950                                 }
41951                             ]
41952                         },
41953                         {
41954                             tag : 'div',
41955                             cls : 'roo-document-slider-next',
41956                             cn : [
41957                                 {
41958                                     tag : 'i',
41959                                     cls : 'fa fa-chevron-right'
41960                                 }
41961                             ]
41962                         }
41963                     ]
41964                 }
41965             ]
41966         };
41967         
41968         return cfg;
41969     },
41970     
41971     initEvents : function()
41972     {
41973         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41974         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41975         
41976         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41977         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41978         
41979         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41980         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41981         
41982         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41983         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41984         
41985         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41986         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41987         
41988         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41989         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41990         
41991         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41992         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41993         
41994         this.thumbEl.on('click', this.onClick, this);
41995         
41996         this.prevIndicator.on('click', this.prev, this);
41997         
41998         this.nextIndicator.on('click', this.next, this);
41999         
42000     },
42001     
42002     initial : function()
42003     {
42004         if(this.files.length){
42005             this.indicator = 1;
42006             this.update()
42007         }
42008         
42009         this.fireEvent('initial', this);
42010     },
42011     
42012     update : function()
42013     {
42014         this.imageEl.attr('src', this.files[this.indicator - 1]);
42015         
42016         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42017         
42018         this.prevIndicator.show();
42019         
42020         if(this.indicator == 1){
42021             this.prevIndicator.hide();
42022         }
42023         
42024         this.nextIndicator.show();
42025         
42026         if(this.indicator == this.files.length){
42027             this.nextIndicator.hide();
42028         }
42029         
42030         this.thumbEl.scrollTo('top');
42031         
42032         this.fireEvent('update', this);
42033     },
42034     
42035     onClick : function(e)
42036     {
42037         e.preventDefault();
42038         
42039         this.fireEvent('click', this);
42040     },
42041     
42042     prev : function(e)
42043     {
42044         e.preventDefault();
42045         
42046         this.indicator = Math.max(1, this.indicator - 1);
42047         
42048         this.update();
42049     },
42050     
42051     next : function(e)
42052     {
42053         e.preventDefault();
42054         
42055         this.indicator = Math.min(this.files.length, this.indicator + 1);
42056         
42057         this.update();
42058     }
42059 });
42060 /*
42061  * - LGPL
42062  *
42063  * RadioSet
42064  *
42065  *
42066  */
42067
42068 /**
42069  * @class Roo.bootstrap.form.RadioSet
42070  * @extends Roo.bootstrap.form.Input
42071  * @children Roo.bootstrap.form.Radio
42072  * Bootstrap RadioSet class
42073  * @cfg {String} indicatorpos (left|right) default left
42074  * @cfg {Boolean} inline (true|false) inline the element (default true)
42075  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42076  * @constructor
42077  * Create a new RadioSet
42078  * @param {Object} config The config object
42079  */
42080
42081 Roo.bootstrap.form.RadioSet = function(config){
42082     
42083     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42084     
42085     this.radioes = [];
42086     
42087     Roo.bootstrap.form.RadioSet.register(this);
42088     
42089     this.addEvents({
42090         /**
42091         * @event check
42092         * Fires when the element is checked or unchecked.
42093         * @param {Roo.bootstrap.form.RadioSet} this This radio
42094         * @param {Roo.bootstrap.form.Radio} item The checked item
42095         */
42096        check : true,
42097        /**
42098         * @event click
42099         * Fires when the element is click.
42100         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42101         * @param {Roo.bootstrap.form.Radio} item The checked item
42102         * @param {Roo.EventObject} e The event object
42103         */
42104        click : true
42105     });
42106     
42107 };
42108
42109 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42110
42111     radioes : false,
42112     
42113     inline : true,
42114     
42115     weight : '',
42116     
42117     indicatorpos : 'left',
42118     
42119     getAutoCreate : function()
42120     {
42121         var label = {
42122             tag : 'label',
42123             cls : 'roo-radio-set-label',
42124             cn : [
42125                 {
42126                     tag : 'span',
42127                     html : this.fieldLabel
42128                 }
42129             ]
42130         };
42131         if (Roo.bootstrap.version == 3) {
42132             
42133             
42134             if(this.indicatorpos == 'left'){
42135                 label.cn.unshift({
42136                     tag : 'i',
42137                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42138                     tooltip : 'This field is required'
42139                 });
42140             } else {
42141                 label.cn.push({
42142                     tag : 'i',
42143                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42144                     tooltip : 'This field is required'
42145                 });
42146             }
42147         }
42148         var items = {
42149             tag : 'div',
42150             cls : 'roo-radio-set-items'
42151         };
42152         
42153         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42154         
42155         if (align === 'left' && this.fieldLabel.length) {
42156             
42157             items = {
42158                 cls : "roo-radio-set-right", 
42159                 cn: [
42160                     items
42161                 ]
42162             };
42163             
42164             if(this.labelWidth > 12){
42165                 label.style = "width: " + this.labelWidth + 'px';
42166             }
42167             
42168             if(this.labelWidth < 13 && this.labelmd == 0){
42169                 this.labelmd = this.labelWidth;
42170             }
42171             
42172             if(this.labellg > 0){
42173                 label.cls += ' col-lg-' + this.labellg;
42174                 items.cls += ' col-lg-' + (12 - this.labellg);
42175             }
42176             
42177             if(this.labelmd > 0){
42178                 label.cls += ' col-md-' + this.labelmd;
42179                 items.cls += ' col-md-' + (12 - this.labelmd);
42180             }
42181             
42182             if(this.labelsm > 0){
42183                 label.cls += ' col-sm-' + this.labelsm;
42184                 items.cls += ' col-sm-' + (12 - this.labelsm);
42185             }
42186             
42187             if(this.labelxs > 0){
42188                 label.cls += ' col-xs-' + this.labelxs;
42189                 items.cls += ' col-xs-' + (12 - this.labelxs);
42190             }
42191         }
42192         
42193         var cfg = {
42194             tag : 'div',
42195             cls : 'roo-radio-set',
42196             cn : [
42197                 {
42198                     tag : 'input',
42199                     cls : 'roo-radio-set-input',
42200                     type : 'hidden',
42201                     name : this.name,
42202                     value : this.value ? this.value :  ''
42203                 },
42204                 label,
42205                 items
42206             ]
42207         };
42208         
42209         if(this.weight.length){
42210             cfg.cls += ' roo-radio-' + this.weight;
42211         }
42212         
42213         if(this.inline) {
42214             cfg.cls += ' roo-radio-set-inline';
42215         }
42216         
42217         var settings=this;
42218         ['xs','sm','md','lg'].map(function(size){
42219             if (settings[size]) {
42220                 cfg.cls += ' col-' + size + '-' + settings[size];
42221             }
42222         });
42223         
42224         return cfg;
42225         
42226     },
42227
42228     initEvents : function()
42229     {
42230         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42231         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42232         
42233         if(!this.fieldLabel.length){
42234             this.labelEl.hide();
42235         }
42236         
42237         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42238         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42239         
42240         this.indicator = this.indicatorEl();
42241         
42242         if(this.indicator){
42243             this.indicator.addClass('invisible');
42244         }
42245         
42246         this.originalValue = this.getValue();
42247         
42248     },
42249     
42250     inputEl: function ()
42251     {
42252         return this.el.select('.roo-radio-set-input', true).first();
42253     },
42254     
42255     getChildContainer : function()
42256     {
42257         return this.itemsEl;
42258     },
42259     
42260     register : function(item)
42261     {
42262         this.radioes.push(item);
42263         
42264     },
42265     
42266     validate : function()
42267     {   
42268         if(this.getVisibilityEl().hasClass('hidden')){
42269             return true;
42270         }
42271         
42272         var valid = false;
42273         
42274         Roo.each(this.radioes, function(i){
42275             if(!i.checked){
42276                 return;
42277             }
42278             
42279             valid = true;
42280             return false;
42281         });
42282         
42283         if(this.allowBlank) {
42284             return true;
42285         }
42286         
42287         if(this.disabled || valid){
42288             this.markValid();
42289             return true;
42290         }
42291         
42292         this.markInvalid();
42293         return false;
42294         
42295     },
42296     
42297     markValid : function()
42298     {
42299         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42300             this.indicatorEl().removeClass('visible');
42301             this.indicatorEl().addClass('invisible');
42302         }
42303         
42304         
42305         if (Roo.bootstrap.version == 3) {
42306             this.el.removeClass([this.invalidClass, this.validClass]);
42307             this.el.addClass(this.validClass);
42308         } else {
42309             this.el.removeClass(['is-invalid','is-valid']);
42310             this.el.addClass(['is-valid']);
42311         }
42312         this.fireEvent('valid', this);
42313     },
42314     
42315     markInvalid : function(msg)
42316     {
42317         if(this.allowBlank || this.disabled){
42318             return;
42319         }
42320         
42321         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42322             this.indicatorEl().removeClass('invisible');
42323             this.indicatorEl().addClass('visible');
42324         }
42325         if (Roo.bootstrap.version == 3) {
42326             this.el.removeClass([this.invalidClass, this.validClass]);
42327             this.el.addClass(this.invalidClass);
42328         } else {
42329             this.el.removeClass(['is-invalid','is-valid']);
42330             this.el.addClass(['is-invalid']);
42331         }
42332         
42333         this.fireEvent('invalid', this, msg);
42334         
42335     },
42336     
42337     setValue : function(v, suppressEvent)
42338     {   
42339         if(this.value === v){
42340             return;
42341         }
42342         
42343         this.value = v;
42344         
42345         if(this.rendered){
42346             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42347         }
42348         
42349         Roo.each(this.radioes, function(i){
42350             i.checked = false;
42351             i.el.removeClass('checked');
42352         });
42353         
42354         Roo.each(this.radioes, function(i){
42355             
42356             if(i.value === v || i.value.toString() === v.toString()){
42357                 i.checked = true;
42358                 i.el.addClass('checked');
42359                 
42360                 if(suppressEvent !== true){
42361                     this.fireEvent('check', this, i);
42362                 }
42363                 
42364                 return false;
42365             }
42366             
42367         }, this);
42368         
42369         this.validate();
42370     },
42371     
42372     clearInvalid : function(){
42373         
42374         if(!this.el || this.preventMark){
42375             return;
42376         }
42377         
42378         this.el.removeClass([this.invalidClass]);
42379         
42380         this.fireEvent('valid', this);
42381     }
42382     
42383 });
42384
42385 Roo.apply(Roo.bootstrap.form.RadioSet, {
42386     
42387     groups: {},
42388     
42389     register : function(set)
42390     {
42391         this.groups[set.name] = set;
42392     },
42393     
42394     get: function(name) 
42395     {
42396         if (typeof(this.groups[name]) == 'undefined') {
42397             return false;
42398         }
42399         
42400         return this.groups[name] ;
42401     }
42402     
42403 });
42404 /*
42405  * Based on:
42406  * Ext JS Library 1.1.1
42407  * Copyright(c) 2006-2007, Ext JS, LLC.
42408  *
42409  * Originally Released Under LGPL - original licence link has changed is not relivant.
42410  *
42411  * Fork - LGPL
42412  * <script type="text/javascript">
42413  */
42414
42415
42416 /**
42417  * @class Roo.bootstrap.SplitBar
42418  * @extends Roo.util.Observable
42419  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42420  * <br><br>
42421  * Usage:
42422  * <pre><code>
42423 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42424                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42425 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42426 split.minSize = 100;
42427 split.maxSize = 600;
42428 split.animate = true;
42429 split.on('moved', splitterMoved);
42430 </code></pre>
42431  * @constructor
42432  * Create a new SplitBar
42433  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42434  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42435  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42436  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42437                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42438                         position of the SplitBar).
42439  */
42440 Roo.bootstrap.SplitBar = function(cfg){
42441     
42442     /** @private */
42443     
42444     //{
42445     //  dragElement : elm
42446     //  resizingElement: el,
42447         // optional..
42448     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42449     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42450         // existingProxy ???
42451     //}
42452     
42453     this.el = Roo.get(cfg.dragElement, true);
42454     this.el.dom.unselectable = "on";
42455     /** @private */
42456     this.resizingEl = Roo.get(cfg.resizingElement, true);
42457
42458     /**
42459      * @private
42460      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42461      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42462      * @type Number
42463      */
42464     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42465     
42466     /**
42467      * The minimum size of the resizing element. (Defaults to 0)
42468      * @type Number
42469      */
42470     this.minSize = 0;
42471     
42472     /**
42473      * The maximum size of the resizing element. (Defaults to 2000)
42474      * @type Number
42475      */
42476     this.maxSize = 2000;
42477     
42478     /**
42479      * Whether to animate the transition to the new size
42480      * @type Boolean
42481      */
42482     this.animate = false;
42483     
42484     /**
42485      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42486      * @type Boolean
42487      */
42488     this.useShim = false;
42489     
42490     /** @private */
42491     this.shim = null;
42492     
42493     if(!cfg.existingProxy){
42494         /** @private */
42495         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42496     }else{
42497         this.proxy = Roo.get(cfg.existingProxy).dom;
42498     }
42499     /** @private */
42500     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42501     
42502     /** @private */
42503     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42504     
42505     /** @private */
42506     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42507     
42508     /** @private */
42509     this.dragSpecs = {};
42510     
42511     /**
42512      * @private The adapter to use to positon and resize elements
42513      */
42514     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42515     this.adapter.init(this);
42516     
42517     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42518         /** @private */
42519         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42520         this.el.addClass("roo-splitbar-h");
42521     }else{
42522         /** @private */
42523         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42524         this.el.addClass("roo-splitbar-v");
42525     }
42526     
42527     this.addEvents({
42528         /**
42529          * @event resize
42530          * Fires when the splitter is moved (alias for {@link #event-moved})
42531          * @param {Roo.bootstrap.SplitBar} this
42532          * @param {Number} newSize the new width or height
42533          */
42534         "resize" : true,
42535         /**
42536          * @event moved
42537          * Fires when the splitter is moved
42538          * @param {Roo.bootstrap.SplitBar} this
42539          * @param {Number} newSize the new width or height
42540          */
42541         "moved" : true,
42542         /**
42543          * @event beforeresize
42544          * Fires before the splitter is dragged
42545          * @param {Roo.bootstrap.SplitBar} this
42546          */
42547         "beforeresize" : true,
42548
42549         "beforeapply" : true
42550     });
42551
42552     Roo.util.Observable.call(this);
42553 };
42554
42555 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42556     onStartProxyDrag : function(x, y){
42557         this.fireEvent("beforeresize", this);
42558         if(!this.overlay){
42559             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42560             o.unselectable();
42561             o.enableDisplayMode("block");
42562             // all splitbars share the same overlay
42563             Roo.bootstrap.SplitBar.prototype.overlay = o;
42564         }
42565         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42566         this.overlay.show();
42567         Roo.get(this.proxy).setDisplayed("block");
42568         var size = this.adapter.getElementSize(this);
42569         this.activeMinSize = this.getMinimumSize();;
42570         this.activeMaxSize = this.getMaximumSize();;
42571         var c1 = size - this.activeMinSize;
42572         var c2 = Math.max(this.activeMaxSize - size, 0);
42573         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42574             this.dd.resetConstraints();
42575             this.dd.setXConstraint(
42576                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42577                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42578             );
42579             this.dd.setYConstraint(0, 0);
42580         }else{
42581             this.dd.resetConstraints();
42582             this.dd.setXConstraint(0, 0);
42583             this.dd.setYConstraint(
42584                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42585                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42586             );
42587          }
42588         this.dragSpecs.startSize = size;
42589         this.dragSpecs.startPoint = [x, y];
42590         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42591     },
42592     
42593     /** 
42594      * @private Called after the drag operation by the DDProxy
42595      */
42596     onEndProxyDrag : function(e){
42597         Roo.get(this.proxy).setDisplayed(false);
42598         var endPoint = Roo.lib.Event.getXY(e);
42599         if(this.overlay){
42600             this.overlay.hide();
42601         }
42602         var newSize;
42603         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42604             newSize = this.dragSpecs.startSize + 
42605                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42606                     endPoint[0] - this.dragSpecs.startPoint[0] :
42607                     this.dragSpecs.startPoint[0] - endPoint[0]
42608                 );
42609         }else{
42610             newSize = this.dragSpecs.startSize + 
42611                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42612                     endPoint[1] - this.dragSpecs.startPoint[1] :
42613                     this.dragSpecs.startPoint[1] - endPoint[1]
42614                 );
42615         }
42616         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42617         if(newSize != this.dragSpecs.startSize){
42618             if(this.fireEvent('beforeapply', this, newSize) !== false){
42619                 this.adapter.setElementSize(this, newSize);
42620                 this.fireEvent("moved", this, newSize);
42621                 this.fireEvent("resize", this, newSize);
42622             }
42623         }
42624     },
42625     
42626     /**
42627      * Get the adapter this SplitBar uses
42628      * @return The adapter object
42629      */
42630     getAdapter : function(){
42631         return this.adapter;
42632     },
42633     
42634     /**
42635      * Set the adapter this SplitBar uses
42636      * @param {Object} adapter A SplitBar adapter object
42637      */
42638     setAdapter : function(adapter){
42639         this.adapter = adapter;
42640         this.adapter.init(this);
42641     },
42642     
42643     /**
42644      * Gets the minimum size for the resizing element
42645      * @return {Number} The minimum size
42646      */
42647     getMinimumSize : function(){
42648         return this.minSize;
42649     },
42650     
42651     /**
42652      * Sets the minimum size for the resizing element
42653      * @param {Number} minSize The minimum size
42654      */
42655     setMinimumSize : function(minSize){
42656         this.minSize = minSize;
42657     },
42658     
42659     /**
42660      * Gets the maximum size for the resizing element
42661      * @return {Number} The maximum size
42662      */
42663     getMaximumSize : function(){
42664         return this.maxSize;
42665     },
42666     
42667     /**
42668      * Sets the maximum size for the resizing element
42669      * @param {Number} maxSize The maximum size
42670      */
42671     setMaximumSize : function(maxSize){
42672         this.maxSize = maxSize;
42673     },
42674     
42675     /**
42676      * Sets the initialize size for the resizing element
42677      * @param {Number} size The initial size
42678      */
42679     setCurrentSize : function(size){
42680         var oldAnimate = this.animate;
42681         this.animate = false;
42682         this.adapter.setElementSize(this, size);
42683         this.animate = oldAnimate;
42684     },
42685     
42686     /**
42687      * Destroy this splitbar. 
42688      * @param {Boolean} removeEl True to remove the element
42689      */
42690     destroy : function(removeEl){
42691         if(this.shim){
42692             this.shim.remove();
42693         }
42694         this.dd.unreg();
42695         this.proxy.parentNode.removeChild(this.proxy);
42696         if(removeEl){
42697             this.el.remove();
42698         }
42699     }
42700 });
42701
42702 /**
42703  * @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.
42704  */
42705 Roo.bootstrap.SplitBar.createProxy = function(dir){
42706     var proxy = new Roo.Element(document.createElement("div"));
42707     proxy.unselectable();
42708     var cls = 'roo-splitbar-proxy';
42709     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42710     document.body.appendChild(proxy.dom);
42711     return proxy.dom;
42712 };
42713
42714 /** 
42715  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42716  * Default Adapter. It assumes the splitter and resizing element are not positioned
42717  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42718  */
42719 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42720 };
42721
42722 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42723     // do nothing for now
42724     init : function(s){
42725     
42726     },
42727     /**
42728      * Called before drag operations to get the current size of the resizing element. 
42729      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42730      */
42731      getElementSize : function(s){
42732         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42733             return s.resizingEl.getWidth();
42734         }else{
42735             return s.resizingEl.getHeight();
42736         }
42737     },
42738     
42739     /**
42740      * Called after drag operations to set the size of the resizing element.
42741      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42742      * @param {Number} newSize The new size to set
42743      * @param {Function} onComplete A function to be invoked when resizing is complete
42744      */
42745     setElementSize : function(s, newSize, onComplete){
42746         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42747             if(!s.animate){
42748                 s.resizingEl.setWidth(newSize);
42749                 if(onComplete){
42750                     onComplete(s, newSize);
42751                 }
42752             }else{
42753                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42754             }
42755         }else{
42756             
42757             if(!s.animate){
42758                 s.resizingEl.setHeight(newSize);
42759                 if(onComplete){
42760                     onComplete(s, newSize);
42761                 }
42762             }else{
42763                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42764             }
42765         }
42766     }
42767 };
42768
42769 /** 
42770  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42771  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42772  * Adapter that  moves the splitter element to align with the resized sizing element. 
42773  * Used with an absolute positioned SplitBar.
42774  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42775  * document.body, make sure you assign an id to the body element.
42776  */
42777 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42778     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42779     this.container = Roo.get(container);
42780 };
42781
42782 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42783     init : function(s){
42784         this.basic.init(s);
42785     },
42786     
42787     getElementSize : function(s){
42788         return this.basic.getElementSize(s);
42789     },
42790     
42791     setElementSize : function(s, newSize, onComplete){
42792         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42793     },
42794     
42795     moveSplitter : function(s){
42796         var yes = Roo.bootstrap.SplitBar;
42797         switch(s.placement){
42798             case yes.LEFT:
42799                 s.el.setX(s.resizingEl.getRight());
42800                 break;
42801             case yes.RIGHT:
42802                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42803                 break;
42804             case yes.TOP:
42805                 s.el.setY(s.resizingEl.getBottom());
42806                 break;
42807             case yes.BOTTOM:
42808                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42809                 break;
42810         }
42811     }
42812 };
42813
42814 /**
42815  * Orientation constant - Create a vertical SplitBar
42816  * @static
42817  * @type Number
42818  */
42819 Roo.bootstrap.SplitBar.VERTICAL = 1;
42820
42821 /**
42822  * Orientation constant - Create a horizontal SplitBar
42823  * @static
42824  * @type Number
42825  */
42826 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42827
42828 /**
42829  * Placement constant - The resizing element is to the left of the splitter element
42830  * @static
42831  * @type Number
42832  */
42833 Roo.bootstrap.SplitBar.LEFT = 1;
42834
42835 /**
42836  * Placement constant - The resizing element is to the right of the splitter element
42837  * @static
42838  * @type Number
42839  */
42840 Roo.bootstrap.SplitBar.RIGHT = 2;
42841
42842 /**
42843  * Placement constant - The resizing element is positioned above the splitter element
42844  * @static
42845  * @type Number
42846  */
42847 Roo.bootstrap.SplitBar.TOP = 3;
42848
42849 /**
42850  * Placement constant - The resizing element is positioned under splitter element
42851  * @static
42852  * @type Number
42853  */
42854 Roo.bootstrap.SplitBar.BOTTOM = 4;
42855 /*
42856  * Based on:
42857  * Ext JS Library 1.1.1
42858  * Copyright(c) 2006-2007, Ext JS, LLC.
42859  *
42860  * Originally Released Under LGPL - original licence link has changed is not relivant.
42861  *
42862  * Fork - LGPL
42863  * <script type="text/javascript">
42864  */
42865
42866 /**
42867  * @class Roo.bootstrap.layout.Manager
42868  * @extends Roo.bootstrap.Component
42869  * @abstract
42870  * Base class for layout managers.
42871  */
42872 Roo.bootstrap.layout.Manager = function(config)
42873 {
42874     this.monitorWindowResize = true; // do this before we apply configuration.
42875     
42876     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42877
42878
42879
42880
42881
42882     /** false to disable window resize monitoring @type Boolean */
42883     
42884     this.regions = {};
42885     this.addEvents({
42886         /**
42887          * @event layout
42888          * Fires when a layout is performed.
42889          * @param {Roo.LayoutManager} this
42890          */
42891         "layout" : true,
42892         /**
42893          * @event regionresized
42894          * Fires when the user resizes a region.
42895          * @param {Roo.LayoutRegion} region The resized region
42896          * @param {Number} newSize The new size (width for east/west, height for north/south)
42897          */
42898         "regionresized" : true,
42899         /**
42900          * @event regioncollapsed
42901          * Fires when a region is collapsed.
42902          * @param {Roo.LayoutRegion} region The collapsed region
42903          */
42904         "regioncollapsed" : true,
42905         /**
42906          * @event regionexpanded
42907          * Fires when a region is expanded.
42908          * @param {Roo.LayoutRegion} region The expanded region
42909          */
42910         "regionexpanded" : true
42911     });
42912     this.updating = false;
42913
42914     if (config.el) {
42915         this.el = Roo.get(config.el);
42916         this.initEvents();
42917     }
42918
42919 };
42920
42921 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42922
42923
42924     regions : null,
42925
42926     monitorWindowResize : true,
42927
42928
42929     updating : false,
42930
42931
42932     onRender : function(ct, position)
42933     {
42934         if(!this.el){
42935             this.el = Roo.get(ct);
42936             this.initEvents();
42937         }
42938         //this.fireEvent('render',this);
42939     },
42940
42941
42942     initEvents: function()
42943     {
42944
42945
42946         // ie scrollbar fix
42947         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42948             document.body.scroll = "no";
42949         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42950             this.el.position('relative');
42951         }
42952         this.id = this.el.id;
42953         this.el.addClass("roo-layout-container");
42954         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42955         if(this.el.dom != document.body ) {
42956             this.el.on('resize', this.layout,this);
42957             this.el.on('show', this.layout,this);
42958         }
42959
42960     },
42961
42962     /**
42963      * Returns true if this layout is currently being updated
42964      * @return {Boolean}
42965      */
42966     isUpdating : function(){
42967         return this.updating;
42968     },
42969
42970     /**
42971      * Suspend the LayoutManager from doing auto-layouts while
42972      * making multiple add or remove calls
42973      */
42974     beginUpdate : function(){
42975         this.updating = true;
42976     },
42977
42978     /**
42979      * Restore auto-layouts and optionally disable the manager from performing a layout
42980      * @param {Boolean} noLayout true to disable a layout update
42981      */
42982     endUpdate : function(noLayout){
42983         this.updating = false;
42984         if(!noLayout){
42985             this.layout();
42986         }
42987     },
42988
42989     layout: function(){
42990         // abstract...
42991     },
42992
42993     onRegionResized : function(region, newSize){
42994         this.fireEvent("regionresized", region, newSize);
42995         this.layout();
42996     },
42997
42998     onRegionCollapsed : function(region){
42999         this.fireEvent("regioncollapsed", region);
43000     },
43001
43002     onRegionExpanded : function(region){
43003         this.fireEvent("regionexpanded", region);
43004     },
43005
43006     /**
43007      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43008      * performs box-model adjustments.
43009      * @return {Object} The size as an object {width: (the width), height: (the height)}
43010      */
43011     getViewSize : function()
43012     {
43013         var size;
43014         if(this.el.dom != document.body){
43015             size = this.el.getSize();
43016         }else{
43017             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43018         }
43019         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43020         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43021         return size;
43022     },
43023
43024     /**
43025      * Returns the Element this layout is bound to.
43026      * @return {Roo.Element}
43027      */
43028     getEl : function(){
43029         return this.el;
43030     },
43031
43032     /**
43033      * Returns the specified region.
43034      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43035      * @return {Roo.LayoutRegion}
43036      */
43037     getRegion : function(target){
43038         return this.regions[target.toLowerCase()];
43039     },
43040
43041     onWindowResize : function(){
43042         if(this.monitorWindowResize){
43043             this.layout();
43044         }
43045     }
43046 });
43047 /*
43048  * Based on:
43049  * Ext JS Library 1.1.1
43050  * Copyright(c) 2006-2007, Ext JS, LLC.
43051  *
43052  * Originally Released Under LGPL - original licence link has changed is not relivant.
43053  *
43054  * Fork - LGPL
43055  * <script type="text/javascript">
43056  */
43057 /**
43058  * @class Roo.bootstrap.layout.Border
43059  * @extends Roo.bootstrap.layout.Manager
43060  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43061  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43062  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43063  * please see: examples/bootstrap/nested.html<br><br>
43064  
43065 <b>The container the layout is rendered into can be either the body element or any other element.
43066 If it is not the body element, the container needs to either be an absolute positioned element,
43067 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43068 the container size if it is not the body element.</b>
43069
43070 * @constructor
43071 * Create a new Border
43072 * @param {Object} config Configuration options
43073  */
43074 Roo.bootstrap.layout.Border = function(config){
43075     config = config || {};
43076     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43077     
43078     
43079     
43080     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43081         if(config[region]){
43082             config[region].region = region;
43083             this.addRegion(config[region]);
43084         }
43085     },this);
43086     
43087 };
43088
43089 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43090
43091 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43092     
43093         /**
43094          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43095          */
43096         /**
43097          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43098          */
43099         /**
43100          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43101          */
43102         /**
43103          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43104          */
43105         /**
43106          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43107          */
43108         
43109         
43110         
43111         
43112     parent : false, // this might point to a 'nest' or a ???
43113     
43114     /**
43115      * Creates and adds a new region if it doesn't already exist.
43116      * @param {String} target The target region key (north, south, east, west or center).
43117      * @param {Object} config The regions config object
43118      * @return {BorderLayoutRegion} The new region
43119      */
43120     addRegion : function(config)
43121     {
43122         if(!this.regions[config.region]){
43123             var r = this.factory(config);
43124             this.bindRegion(r);
43125         }
43126         return this.regions[config.region];
43127     },
43128
43129     // private (kinda)
43130     bindRegion : function(r){
43131         this.regions[r.config.region] = r;
43132         
43133         r.on("visibilitychange",    this.layout, this);
43134         r.on("paneladded",          this.layout, this);
43135         r.on("panelremoved",        this.layout, this);
43136         r.on("invalidated",         this.layout, this);
43137         r.on("resized",             this.onRegionResized, this);
43138         r.on("collapsed",           this.onRegionCollapsed, this);
43139         r.on("expanded",            this.onRegionExpanded, this);
43140     },
43141
43142     /**
43143      * Performs a layout update.
43144      */
43145     layout : function()
43146     {
43147         if(this.updating) {
43148             return;
43149         }
43150         
43151         // render all the rebions if they have not been done alreayd?
43152         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43153             if(this.regions[region] && !this.regions[region].bodyEl){
43154                 this.regions[region].onRender(this.el)
43155             }
43156         },this);
43157         
43158         var size = this.getViewSize();
43159         var w = size.width;
43160         var h = size.height;
43161         var centerW = w;
43162         var centerH = h;
43163         var centerY = 0;
43164         var centerX = 0;
43165         //var x = 0, y = 0;
43166
43167         var rs = this.regions;
43168         var north = rs["north"];
43169         var south = rs["south"]; 
43170         var west = rs["west"];
43171         var east = rs["east"];
43172         var center = rs["center"];
43173         //if(this.hideOnLayout){ // not supported anymore
43174             //c.el.setStyle("display", "none");
43175         //}
43176         if(north && north.isVisible()){
43177             var b = north.getBox();
43178             var m = north.getMargins();
43179             b.width = w - (m.left+m.right);
43180             b.x = m.left;
43181             b.y = m.top;
43182             centerY = b.height + b.y + m.bottom;
43183             centerH -= centerY;
43184             north.updateBox(this.safeBox(b));
43185         }
43186         if(south && south.isVisible()){
43187             var b = south.getBox();
43188             var m = south.getMargins();
43189             b.width = w - (m.left+m.right);
43190             b.x = m.left;
43191             var totalHeight = (b.height + m.top + m.bottom);
43192             b.y = h - totalHeight + m.top;
43193             centerH -= totalHeight;
43194             south.updateBox(this.safeBox(b));
43195         }
43196         if(west && west.isVisible()){
43197             var b = west.getBox();
43198             var m = west.getMargins();
43199             b.height = centerH - (m.top+m.bottom);
43200             b.x = m.left;
43201             b.y = centerY + m.top;
43202             var totalWidth = (b.width + m.left + m.right);
43203             centerX += totalWidth;
43204             centerW -= totalWidth;
43205             west.updateBox(this.safeBox(b));
43206         }
43207         if(east && east.isVisible()){
43208             var b = east.getBox();
43209             var m = east.getMargins();
43210             b.height = centerH - (m.top+m.bottom);
43211             var totalWidth = (b.width + m.left + m.right);
43212             b.x = w - totalWidth + m.left;
43213             b.y = centerY + m.top;
43214             centerW -= totalWidth;
43215             east.updateBox(this.safeBox(b));
43216         }
43217         if(center){
43218             var m = center.getMargins();
43219             var centerBox = {
43220                 x: centerX + m.left,
43221                 y: centerY + m.top,
43222                 width: centerW - (m.left+m.right),
43223                 height: centerH - (m.top+m.bottom)
43224             };
43225             //if(this.hideOnLayout){
43226                 //center.el.setStyle("display", "block");
43227             //}
43228             center.updateBox(this.safeBox(centerBox));
43229         }
43230         this.el.repaint();
43231         this.fireEvent("layout", this);
43232     },
43233
43234     // private
43235     safeBox : function(box){
43236         box.width = Math.max(0, box.width);
43237         box.height = Math.max(0, box.height);
43238         return box;
43239     },
43240
43241     /**
43242      * Adds a ContentPanel (or subclass) to this layout.
43243      * @param {String} target The target region key (north, south, east, west or center).
43244      * @param {Roo.ContentPanel} panel The panel to add
43245      * @return {Roo.ContentPanel} The added panel
43246      */
43247     add : function(target, panel){
43248          
43249         target = target.toLowerCase();
43250         return this.regions[target].add(panel);
43251     },
43252
43253     /**
43254      * Remove a ContentPanel (or subclass) to this layout.
43255      * @param {String} target The target region key (north, south, east, west or center).
43256      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43257      * @return {Roo.ContentPanel} The removed panel
43258      */
43259     remove : function(target, panel){
43260         target = target.toLowerCase();
43261         return this.regions[target].remove(panel);
43262     },
43263
43264     /**
43265      * Searches all regions for a panel with the specified id
43266      * @param {String} panelId
43267      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43268      */
43269     findPanel : function(panelId){
43270         var rs = this.regions;
43271         for(var target in rs){
43272             if(typeof rs[target] != "function"){
43273                 var p = rs[target].getPanel(panelId);
43274                 if(p){
43275                     return p;
43276                 }
43277             }
43278         }
43279         return null;
43280     },
43281
43282     /**
43283      * Searches all regions for a panel with the specified id and activates (shows) it.
43284      * @param {String/ContentPanel} panelId The panels id or the panel itself
43285      * @return {Roo.ContentPanel} The shown panel or null
43286      */
43287     showPanel : function(panelId) {
43288       var rs = this.regions;
43289       for(var target in rs){
43290          var r = rs[target];
43291          if(typeof r != "function"){
43292             if(r.hasPanel(panelId)){
43293                return r.showPanel(panelId);
43294             }
43295          }
43296       }
43297       return null;
43298    },
43299
43300    /**
43301      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43302      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43303      */
43304    /*
43305     restoreState : function(provider){
43306         if(!provider){
43307             provider = Roo.state.Manager;
43308         }
43309         var sm = new Roo.LayoutStateManager();
43310         sm.init(this, provider);
43311     },
43312 */
43313  
43314  
43315     /**
43316      * Adds a xtype elements to the layout.
43317      * <pre><code>
43318
43319 layout.addxtype({
43320        xtype : 'ContentPanel',
43321        region: 'west',
43322        items: [ .... ]
43323    }
43324 );
43325
43326 layout.addxtype({
43327         xtype : 'NestedLayoutPanel',
43328         region: 'west',
43329         layout: {
43330            center: { },
43331            west: { }   
43332         },
43333         items : [ ... list of content panels or nested layout panels.. ]
43334    }
43335 );
43336 </code></pre>
43337      * @param {Object} cfg Xtype definition of item to add.
43338      */
43339     addxtype : function(cfg)
43340     {
43341         // basically accepts a pannel...
43342         // can accept a layout region..!?!?
43343         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43344         
43345         
43346         // theory?  children can only be panels??
43347         
43348         //if (!cfg.xtype.match(/Panel$/)) {
43349         //    return false;
43350         //}
43351         var ret = false;
43352         
43353         if (typeof(cfg.region) == 'undefined') {
43354             Roo.log("Failed to add Panel, region was not set");
43355             Roo.log(cfg);
43356             return false;
43357         }
43358         var region = cfg.region;
43359         delete cfg.region;
43360         
43361           
43362         var xitems = [];
43363         if (cfg.items) {
43364             xitems = cfg.items;
43365             delete cfg.items;
43366         }
43367         var nb = false;
43368         
43369         if ( region == 'center') {
43370             Roo.log("Center: " + cfg.title);
43371         }
43372         
43373         
43374         switch(cfg.xtype) 
43375         {
43376             case 'Content':  // ContentPanel (el, cfg)
43377             case 'Scroll':  // ContentPanel (el, cfg)
43378             case 'View': 
43379                 cfg.autoCreate = cfg.autoCreate || true;
43380                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43381                 //} else {
43382                 //    var el = this.el.createChild();
43383                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43384                 //}
43385                 
43386                 this.add(region, ret);
43387                 break;
43388             
43389             /*
43390             case 'TreePanel': // our new panel!
43391                 cfg.el = this.el.createChild();
43392                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43393                 this.add(region, ret);
43394                 break;
43395             */
43396             
43397             case 'Nest': 
43398                 // create a new Layout (which is  a Border Layout...
43399                 
43400                 var clayout = cfg.layout;
43401                 clayout.el  = this.el.createChild();
43402                 clayout.items   = clayout.items  || [];
43403                 
43404                 delete cfg.layout;
43405                 
43406                 // replace this exitems with the clayout ones..
43407                 xitems = clayout.items;
43408                  
43409                 // force background off if it's in center...
43410                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43411                     cfg.background = false;
43412                 }
43413                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43414                 
43415                 
43416                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43417                 //console.log('adding nested layout panel '  + cfg.toSource());
43418                 this.add(region, ret);
43419                 nb = {}; /// find first...
43420                 break;
43421             
43422             case 'Grid':
43423                 
43424                 // needs grid and region
43425                 
43426                 //var el = this.getRegion(region).el.createChild();
43427                 /*
43428                  *var el = this.el.createChild();
43429                 // create the grid first...
43430                 cfg.grid.container = el;
43431                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43432                 */
43433                 
43434                 if (region == 'center' && this.active ) {
43435                     cfg.background = false;
43436                 }
43437                 
43438                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43439                 
43440                 this.add(region, ret);
43441                 /*
43442                 if (cfg.background) {
43443                     // render grid on panel activation (if panel background)
43444                     ret.on('activate', function(gp) {
43445                         if (!gp.grid.rendered) {
43446                     //        gp.grid.render(el);
43447                         }
43448                     });
43449                 } else {
43450                   //  cfg.grid.render(el);
43451                 }
43452                 */
43453                 break;
43454            
43455            
43456             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43457                 // it was the old xcomponent building that caused this before.
43458                 // espeically if border is the top element in the tree.
43459                 ret = this;
43460                 break; 
43461                 
43462                     
43463                 
43464                 
43465                 
43466             default:
43467                 /*
43468                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43469                     
43470                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43471                     this.add(region, ret);
43472                 } else {
43473                 */
43474                     Roo.log(cfg);
43475                     throw "Can not add '" + cfg.xtype + "' to Border";
43476                     return null;
43477              
43478                                 
43479              
43480         }
43481         this.beginUpdate();
43482         // add children..
43483         var region = '';
43484         var abn = {};
43485         Roo.each(xitems, function(i)  {
43486             region = nb && i.region ? i.region : false;
43487             
43488             var add = ret.addxtype(i);
43489            
43490             if (region) {
43491                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43492                 if (!i.background) {
43493                     abn[region] = nb[region] ;
43494                 }
43495             }
43496             
43497         });
43498         this.endUpdate();
43499
43500         // make the last non-background panel active..
43501         //if (nb) { Roo.log(abn); }
43502         if (nb) {
43503             
43504             for(var r in abn) {
43505                 region = this.getRegion(r);
43506                 if (region) {
43507                     // tried using nb[r], but it does not work..
43508                      
43509                     region.showPanel(abn[r]);
43510                    
43511                 }
43512             }
43513         }
43514         return ret;
43515         
43516     },
43517     
43518     
43519 // private
43520     factory : function(cfg)
43521     {
43522         
43523         var validRegions = Roo.bootstrap.layout.Border.regions;
43524
43525         var target = cfg.region;
43526         cfg.mgr = this;
43527         
43528         var r = Roo.bootstrap.layout;
43529         Roo.log(target);
43530         switch(target){
43531             case "north":
43532                 return new r.North(cfg);
43533             case "south":
43534                 return new r.South(cfg);
43535             case "east":
43536                 return new r.East(cfg);
43537             case "west":
43538                 return new r.West(cfg);
43539             case "center":
43540                 return new r.Center(cfg);
43541         }
43542         throw 'Layout region "'+target+'" not supported.';
43543     }
43544     
43545     
43546 });
43547  /*
43548  * Based on:
43549  * Ext JS Library 1.1.1
43550  * Copyright(c) 2006-2007, Ext JS, LLC.
43551  *
43552  * Originally Released Under LGPL - original licence link has changed is not relivant.
43553  *
43554  * Fork - LGPL
43555  * <script type="text/javascript">
43556  */
43557  
43558 /**
43559  * @class Roo.bootstrap.layout.Basic
43560  * @extends Roo.util.Observable
43561  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43562  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43563  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43564  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43565  * @cfg {string}   region  the region that it inhabits..
43566  * @cfg {bool}   skipConfig skip config?
43567  * 
43568
43569  */
43570 Roo.bootstrap.layout.Basic = function(config){
43571     
43572     this.mgr = config.mgr;
43573     
43574     this.position = config.region;
43575     
43576     var skipConfig = config.skipConfig;
43577     
43578     this.events = {
43579         /**
43580          * @scope Roo.BasicLayoutRegion
43581          */
43582         
43583         /**
43584          * @event beforeremove
43585          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43586          * @param {Roo.LayoutRegion} this
43587          * @param {Roo.ContentPanel} panel The panel
43588          * @param {Object} e The cancel event object
43589          */
43590         "beforeremove" : true,
43591         /**
43592          * @event invalidated
43593          * Fires when the layout for this region is changed.
43594          * @param {Roo.LayoutRegion} this
43595          */
43596         "invalidated" : true,
43597         /**
43598          * @event visibilitychange
43599          * Fires when this region is shown or hidden 
43600          * @param {Roo.LayoutRegion} this
43601          * @param {Boolean} visibility true or false
43602          */
43603         "visibilitychange" : true,
43604         /**
43605          * @event paneladded
43606          * Fires when a panel is added. 
43607          * @param {Roo.LayoutRegion} this
43608          * @param {Roo.ContentPanel} panel The panel
43609          */
43610         "paneladded" : true,
43611         /**
43612          * @event panelremoved
43613          * Fires when a panel is removed. 
43614          * @param {Roo.LayoutRegion} this
43615          * @param {Roo.ContentPanel} panel The panel
43616          */
43617         "panelremoved" : true,
43618         /**
43619          * @event beforecollapse
43620          * Fires when this region before collapse.
43621          * @param {Roo.LayoutRegion} this
43622          */
43623         "beforecollapse" : true,
43624         /**
43625          * @event collapsed
43626          * Fires when this region is collapsed.
43627          * @param {Roo.LayoutRegion} this
43628          */
43629         "collapsed" : true,
43630         /**
43631          * @event expanded
43632          * Fires when this region is expanded.
43633          * @param {Roo.LayoutRegion} this
43634          */
43635         "expanded" : true,
43636         /**
43637          * @event slideshow
43638          * Fires when this region is slid into view.
43639          * @param {Roo.LayoutRegion} this
43640          */
43641         "slideshow" : true,
43642         /**
43643          * @event slidehide
43644          * Fires when this region slides out of view. 
43645          * @param {Roo.LayoutRegion} this
43646          */
43647         "slidehide" : true,
43648         /**
43649          * @event panelactivated
43650          * Fires when a panel is activated. 
43651          * @param {Roo.LayoutRegion} this
43652          * @param {Roo.ContentPanel} panel The activated panel
43653          */
43654         "panelactivated" : true,
43655         /**
43656          * @event resized
43657          * Fires when the user resizes this region. 
43658          * @param {Roo.LayoutRegion} this
43659          * @param {Number} newSize The new size (width for east/west, height for north/south)
43660          */
43661         "resized" : true
43662     };
43663     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43664     this.panels = new Roo.util.MixedCollection();
43665     this.panels.getKey = this.getPanelId.createDelegate(this);
43666     this.box = null;
43667     this.activePanel = null;
43668     // ensure listeners are added...
43669     
43670     if (config.listeners || config.events) {
43671         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43672             listeners : config.listeners || {},
43673             events : config.events || {}
43674         });
43675     }
43676     
43677     if(skipConfig !== true){
43678         this.applyConfig(config);
43679     }
43680 };
43681
43682 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43683 {
43684     getPanelId : function(p){
43685         return p.getId();
43686     },
43687     
43688     applyConfig : function(config){
43689         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43690         this.config = config;
43691         
43692     },
43693     
43694     /**
43695      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43696      * the width, for horizontal (north, south) the height.
43697      * @param {Number} newSize The new width or height
43698      */
43699     resizeTo : function(newSize){
43700         var el = this.el ? this.el :
43701                  (this.activePanel ? this.activePanel.getEl() : null);
43702         if(el){
43703             switch(this.position){
43704                 case "east":
43705                 case "west":
43706                     el.setWidth(newSize);
43707                     this.fireEvent("resized", this, newSize);
43708                 break;
43709                 case "north":
43710                 case "south":
43711                     el.setHeight(newSize);
43712                     this.fireEvent("resized", this, newSize);
43713                 break;                
43714             }
43715         }
43716     },
43717     
43718     getBox : function(){
43719         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43720     },
43721     
43722     getMargins : function(){
43723         return this.margins;
43724     },
43725     
43726     updateBox : function(box){
43727         this.box = box;
43728         var el = this.activePanel.getEl();
43729         el.dom.style.left = box.x + "px";
43730         el.dom.style.top = box.y + "px";
43731         this.activePanel.setSize(box.width, box.height);
43732     },
43733     
43734     /**
43735      * Returns the container element for this region.
43736      * @return {Roo.Element}
43737      */
43738     getEl : function(){
43739         return this.activePanel;
43740     },
43741     
43742     /**
43743      * Returns true if this region is currently visible.
43744      * @return {Boolean}
43745      */
43746     isVisible : function(){
43747         return this.activePanel ? true : false;
43748     },
43749     
43750     setActivePanel : function(panel){
43751         panel = this.getPanel(panel);
43752         if(this.activePanel && this.activePanel != panel){
43753             this.activePanel.setActiveState(false);
43754             this.activePanel.getEl().setLeftTop(-10000,-10000);
43755         }
43756         this.activePanel = panel;
43757         panel.setActiveState(true);
43758         if(this.box){
43759             panel.setSize(this.box.width, this.box.height);
43760         }
43761         this.fireEvent("panelactivated", this, panel);
43762         this.fireEvent("invalidated");
43763     },
43764     
43765     /**
43766      * Show the specified panel.
43767      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43768      * @return {Roo.ContentPanel} The shown panel or null
43769      */
43770     showPanel : function(panel){
43771         panel = this.getPanel(panel);
43772         if(panel){
43773             this.setActivePanel(panel);
43774         }
43775         return panel;
43776     },
43777     
43778     /**
43779      * Get the active panel for this region.
43780      * @return {Roo.ContentPanel} The active panel or null
43781      */
43782     getActivePanel : function(){
43783         return this.activePanel;
43784     },
43785     
43786     /**
43787      * Add the passed ContentPanel(s)
43788      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43789      * @return {Roo.ContentPanel} The panel added (if only one was added)
43790      */
43791     add : function(panel){
43792         if(arguments.length > 1){
43793             for(var i = 0, len = arguments.length; i < len; i++) {
43794                 this.add(arguments[i]);
43795             }
43796             return null;
43797         }
43798         if(this.hasPanel(panel)){
43799             this.showPanel(panel);
43800             return panel;
43801         }
43802         var el = panel.getEl();
43803         if(el.dom.parentNode != this.mgr.el.dom){
43804             this.mgr.el.dom.appendChild(el.dom);
43805         }
43806         if(panel.setRegion){
43807             panel.setRegion(this);
43808         }
43809         this.panels.add(panel);
43810         el.setStyle("position", "absolute");
43811         if(!panel.background){
43812             this.setActivePanel(panel);
43813             if(this.config.initialSize && this.panels.getCount()==1){
43814                 this.resizeTo(this.config.initialSize);
43815             }
43816         }
43817         this.fireEvent("paneladded", this, panel);
43818         return panel;
43819     },
43820     
43821     /**
43822      * Returns true if the panel is in this region.
43823      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43824      * @return {Boolean}
43825      */
43826     hasPanel : function(panel){
43827         if(typeof panel == "object"){ // must be panel obj
43828             panel = panel.getId();
43829         }
43830         return this.getPanel(panel) ? true : false;
43831     },
43832     
43833     /**
43834      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43835      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43836      * @param {Boolean} preservePanel Overrides the config preservePanel option
43837      * @return {Roo.ContentPanel} The panel that was removed
43838      */
43839     remove : function(panel, preservePanel){
43840         panel = this.getPanel(panel);
43841         if(!panel){
43842             return null;
43843         }
43844         var e = {};
43845         this.fireEvent("beforeremove", this, panel, e);
43846         if(e.cancel === true){
43847             return null;
43848         }
43849         var panelId = panel.getId();
43850         this.panels.removeKey(panelId);
43851         return panel;
43852     },
43853     
43854     /**
43855      * Returns the panel specified or null if it's not in this region.
43856      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43857      * @return {Roo.ContentPanel}
43858      */
43859     getPanel : function(id){
43860         if(typeof id == "object"){ // must be panel obj
43861             return id;
43862         }
43863         return this.panels.get(id);
43864     },
43865     
43866     /**
43867      * Returns this regions position (north/south/east/west/center).
43868      * @return {String} 
43869      */
43870     getPosition: function(){
43871         return this.position;    
43872     }
43873 });/*
43874  * Based on:
43875  * Ext JS Library 1.1.1
43876  * Copyright(c) 2006-2007, Ext JS, LLC.
43877  *
43878  * Originally Released Under LGPL - original licence link has changed is not relivant.
43879  *
43880  * Fork - LGPL
43881  * <script type="text/javascript">
43882  */
43883  
43884 /**
43885  * @class Roo.bootstrap.layout.Region
43886  * @extends Roo.bootstrap.layout.Basic
43887  * This class represents a region in a layout manager.
43888  
43889  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43890  * @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})
43891  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43892  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43893  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43894  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43895  * @cfg {String}    title           The title for the region (overrides panel titles)
43896  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43897  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43898  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43899  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43900  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43901  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43902  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43903  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43904  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43905  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43906
43907  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43908  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43909  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43910  * @cfg {Number}    width           For East/West panels
43911  * @cfg {Number}    height          For North/South panels
43912  * @cfg {Boolean}   split           To show the splitter
43913  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43914  * 
43915  * @cfg {string}   cls             Extra CSS classes to add to region
43916  * 
43917  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43918  * @cfg {string}   region  the region that it inhabits..
43919  *
43920
43921  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43922  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43923
43924  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43925  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43926  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43927  */
43928 Roo.bootstrap.layout.Region = function(config)
43929 {
43930     this.applyConfig(config);
43931
43932     var mgr = config.mgr;
43933     var pos = config.region;
43934     config.skipConfig = true;
43935     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43936     
43937     if (mgr.el) {
43938         this.onRender(mgr.el);   
43939     }
43940      
43941     this.visible = true;
43942     this.collapsed = false;
43943     this.unrendered_panels = [];
43944 };
43945
43946 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43947
43948     position: '', // set by wrapper (eg. north/south etc..)
43949     unrendered_panels : null,  // unrendered panels.
43950     
43951     tabPosition : false,
43952     
43953     mgr: false, // points to 'Border'
43954     
43955     
43956     createBody : function(){
43957         /** This region's body element 
43958         * @type Roo.Element */
43959         this.bodyEl = this.el.createChild({
43960                 tag: "div",
43961                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43962         });
43963     },
43964
43965     onRender: function(ctr, pos)
43966     {
43967         var dh = Roo.DomHelper;
43968         /** This region's container element 
43969         * @type Roo.Element */
43970         this.el = dh.append(ctr.dom, {
43971                 tag: "div",
43972                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43973             }, true);
43974         /** This region's title element 
43975         * @type Roo.Element */
43976     
43977         this.titleEl = dh.append(this.el.dom,  {
43978                 tag: "div",
43979                 unselectable: "on",
43980                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43981                 children:[
43982                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43983                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43984                 ]
43985             }, true);
43986         
43987         this.titleEl.enableDisplayMode();
43988         /** This region's title text element 
43989         * @type HTMLElement */
43990         this.titleTextEl = this.titleEl.dom.firstChild;
43991         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43992         /*
43993         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43994         this.closeBtn.enableDisplayMode();
43995         this.closeBtn.on("click", this.closeClicked, this);
43996         this.closeBtn.hide();
43997     */
43998         this.createBody(this.config);
43999         if(this.config.hideWhenEmpty){
44000             this.hide();
44001             this.on("paneladded", this.validateVisibility, this);
44002             this.on("panelremoved", this.validateVisibility, this);
44003         }
44004         if(this.autoScroll){
44005             this.bodyEl.setStyle("overflow", "auto");
44006         }else{
44007             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44008         }
44009         //if(c.titlebar !== false){
44010             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44011                 this.titleEl.hide();
44012             }else{
44013                 this.titleEl.show();
44014                 if(this.config.title){
44015                     this.titleTextEl.innerHTML = this.config.title;
44016                 }
44017             }
44018         //}
44019         if(this.config.collapsed){
44020             this.collapse(true);
44021         }
44022         if(this.config.hidden){
44023             this.hide();
44024         }
44025         
44026         if (this.unrendered_panels && this.unrendered_panels.length) {
44027             for (var i =0;i< this.unrendered_panels.length; i++) {
44028                 this.add(this.unrendered_panels[i]);
44029             }
44030             this.unrendered_panels = null;
44031             
44032         }
44033         
44034     },
44035     
44036     applyConfig : function(c)
44037     {
44038         /*
44039          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44040             var dh = Roo.DomHelper;
44041             if(c.titlebar !== false){
44042                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44043                 this.collapseBtn.on("click", this.collapse, this);
44044                 this.collapseBtn.enableDisplayMode();
44045                 /*
44046                 if(c.showPin === true || this.showPin){
44047                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44048                     this.stickBtn.enableDisplayMode();
44049                     this.stickBtn.on("click", this.expand, this);
44050                     this.stickBtn.hide();
44051                 }
44052                 
44053             }
44054             */
44055             /** This region's collapsed element
44056             * @type Roo.Element */
44057             /*
44058              *
44059             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44060                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44061             ]}, true);
44062             
44063             if(c.floatable !== false){
44064                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44065                this.collapsedEl.on("click", this.collapseClick, this);
44066             }
44067
44068             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44069                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44070                    id: "message", unselectable: "on", style:{"float":"left"}});
44071                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44072              }
44073             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44074             this.expandBtn.on("click", this.expand, this);
44075             
44076         }
44077         
44078         if(this.collapseBtn){
44079             this.collapseBtn.setVisible(c.collapsible == true);
44080         }
44081         
44082         this.cmargins = c.cmargins || this.cmargins ||
44083                          (this.position == "west" || this.position == "east" ?
44084                              {top: 0, left: 2, right:2, bottom: 0} :
44085                              {top: 2, left: 0, right:0, bottom: 2});
44086         */
44087         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44088         
44089         
44090         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44091         
44092         this.autoScroll = c.autoScroll || false;
44093         
44094         
44095        
44096         
44097         this.duration = c.duration || .30;
44098         this.slideDuration = c.slideDuration || .45;
44099         this.config = c;
44100        
44101     },
44102     /**
44103      * Returns true if this region is currently visible.
44104      * @return {Boolean}
44105      */
44106     isVisible : function(){
44107         return this.visible;
44108     },
44109
44110     /**
44111      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44112      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44113      */
44114     //setCollapsedTitle : function(title){
44115     //    title = title || "&#160;";
44116      //   if(this.collapsedTitleTextEl){
44117       //      this.collapsedTitleTextEl.innerHTML = title;
44118        // }
44119     //},
44120
44121     getBox : function(){
44122         var b;
44123       //  if(!this.collapsed){
44124             b = this.el.getBox(false, true);
44125        // }else{
44126           //  b = this.collapsedEl.getBox(false, true);
44127         //}
44128         return b;
44129     },
44130
44131     getMargins : function(){
44132         return this.margins;
44133         //return this.collapsed ? this.cmargins : this.margins;
44134     },
44135 /*
44136     highlight : function(){
44137         this.el.addClass("x-layout-panel-dragover");
44138     },
44139
44140     unhighlight : function(){
44141         this.el.removeClass("x-layout-panel-dragover");
44142     },
44143 */
44144     updateBox : function(box)
44145     {
44146         if (!this.bodyEl) {
44147             return; // not rendered yet..
44148         }
44149         
44150         this.box = box;
44151         if(!this.collapsed){
44152             this.el.dom.style.left = box.x + "px";
44153             this.el.dom.style.top = box.y + "px";
44154             this.updateBody(box.width, box.height);
44155         }else{
44156             this.collapsedEl.dom.style.left = box.x + "px";
44157             this.collapsedEl.dom.style.top = box.y + "px";
44158             this.collapsedEl.setSize(box.width, box.height);
44159         }
44160         if(this.tabs){
44161             this.tabs.autoSizeTabs();
44162         }
44163     },
44164
44165     updateBody : function(w, h)
44166     {
44167         if(w !== null){
44168             this.el.setWidth(w);
44169             w -= this.el.getBorderWidth("rl");
44170             if(this.config.adjustments){
44171                 w += this.config.adjustments[0];
44172             }
44173         }
44174         if(h !== null && h > 0){
44175             this.el.setHeight(h);
44176             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44177             h -= this.el.getBorderWidth("tb");
44178             if(this.config.adjustments){
44179                 h += this.config.adjustments[1];
44180             }
44181             this.bodyEl.setHeight(h);
44182             if(this.tabs){
44183                 h = this.tabs.syncHeight(h);
44184             }
44185         }
44186         if(this.panelSize){
44187             w = w !== null ? w : this.panelSize.width;
44188             h = h !== null ? h : this.panelSize.height;
44189         }
44190         if(this.activePanel){
44191             var el = this.activePanel.getEl();
44192             w = w !== null ? w : el.getWidth();
44193             h = h !== null ? h : el.getHeight();
44194             this.panelSize = {width: w, height: h};
44195             this.activePanel.setSize(w, h);
44196         }
44197         if(Roo.isIE && this.tabs){
44198             this.tabs.el.repaint();
44199         }
44200     },
44201
44202     /**
44203      * Returns the container element for this region.
44204      * @return {Roo.Element}
44205      */
44206     getEl : function(){
44207         return this.el;
44208     },
44209
44210     /**
44211      * Hides this region.
44212      */
44213     hide : function(){
44214         //if(!this.collapsed){
44215             this.el.dom.style.left = "-2000px";
44216             this.el.hide();
44217         //}else{
44218          //   this.collapsedEl.dom.style.left = "-2000px";
44219          //   this.collapsedEl.hide();
44220        // }
44221         this.visible = false;
44222         this.fireEvent("visibilitychange", this, false);
44223     },
44224
44225     /**
44226      * Shows this region if it was previously hidden.
44227      */
44228     show : function(){
44229         //if(!this.collapsed){
44230             this.el.show();
44231         //}else{
44232         //    this.collapsedEl.show();
44233        // }
44234         this.visible = true;
44235         this.fireEvent("visibilitychange", this, true);
44236     },
44237 /*
44238     closeClicked : function(){
44239         if(this.activePanel){
44240             this.remove(this.activePanel);
44241         }
44242     },
44243
44244     collapseClick : function(e){
44245         if(this.isSlid){
44246            e.stopPropagation();
44247            this.slideIn();
44248         }else{
44249            e.stopPropagation();
44250            this.slideOut();
44251         }
44252     },
44253 */
44254     /**
44255      * Collapses this region.
44256      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44257      */
44258     /*
44259     collapse : function(skipAnim, skipCheck = false){
44260         if(this.collapsed) {
44261             return;
44262         }
44263         
44264         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44265             
44266             this.collapsed = true;
44267             if(this.split){
44268                 this.split.el.hide();
44269             }
44270             if(this.config.animate && skipAnim !== true){
44271                 this.fireEvent("invalidated", this);
44272                 this.animateCollapse();
44273             }else{
44274                 this.el.setLocation(-20000,-20000);
44275                 this.el.hide();
44276                 this.collapsedEl.show();
44277                 this.fireEvent("collapsed", this);
44278                 this.fireEvent("invalidated", this);
44279             }
44280         }
44281         
44282     },
44283 */
44284     animateCollapse : function(){
44285         // overridden
44286     },
44287
44288     /**
44289      * Expands this region if it was previously collapsed.
44290      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44291      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44292      */
44293     /*
44294     expand : function(e, skipAnim){
44295         if(e) {
44296             e.stopPropagation();
44297         }
44298         if(!this.collapsed || this.el.hasActiveFx()) {
44299             return;
44300         }
44301         if(this.isSlid){
44302             this.afterSlideIn();
44303             skipAnim = true;
44304         }
44305         this.collapsed = false;
44306         if(this.config.animate && skipAnim !== true){
44307             this.animateExpand();
44308         }else{
44309             this.el.show();
44310             if(this.split){
44311                 this.split.el.show();
44312             }
44313             this.collapsedEl.setLocation(-2000,-2000);
44314             this.collapsedEl.hide();
44315             this.fireEvent("invalidated", this);
44316             this.fireEvent("expanded", this);
44317         }
44318     },
44319 */
44320     animateExpand : function(){
44321         // overridden
44322     },
44323
44324     initTabs : function()
44325     {
44326         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44327         
44328         var ts = new Roo.bootstrap.panel.Tabs({
44329             el: this.bodyEl.dom,
44330             region : this,
44331             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44332             disableTooltips: this.config.disableTabTips,
44333             toolbar : this.config.toolbar
44334         });
44335         
44336         if(this.config.hideTabs){
44337             ts.stripWrap.setDisplayed(false);
44338         }
44339         this.tabs = ts;
44340         ts.resizeTabs = this.config.resizeTabs === true;
44341         ts.minTabWidth = this.config.minTabWidth || 40;
44342         ts.maxTabWidth = this.config.maxTabWidth || 250;
44343         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44344         ts.monitorResize = false;
44345         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44346         ts.bodyEl.addClass('roo-layout-tabs-body');
44347         this.panels.each(this.initPanelAsTab, this);
44348     },
44349
44350     initPanelAsTab : function(panel){
44351         var ti = this.tabs.addTab(
44352             panel.getEl().id,
44353             panel.getTitle(),
44354             null,
44355             this.config.closeOnTab && panel.isClosable(),
44356             panel.tpl
44357         );
44358         if(panel.tabTip !== undefined){
44359             ti.setTooltip(panel.tabTip);
44360         }
44361         ti.on("activate", function(){
44362               this.setActivePanel(panel);
44363         }, this);
44364         
44365         if(this.config.closeOnTab){
44366             ti.on("beforeclose", function(t, e){
44367                 e.cancel = true;
44368                 this.remove(panel);
44369             }, this);
44370         }
44371         
44372         panel.tabItem = ti;
44373         
44374         return ti;
44375     },
44376
44377     updatePanelTitle : function(panel, title)
44378     {
44379         if(this.activePanel == panel){
44380             this.updateTitle(title);
44381         }
44382         if(this.tabs){
44383             var ti = this.tabs.getTab(panel.getEl().id);
44384             ti.setText(title);
44385             if(panel.tabTip !== undefined){
44386                 ti.setTooltip(panel.tabTip);
44387             }
44388         }
44389     },
44390
44391     updateTitle : function(title){
44392         if(this.titleTextEl && !this.config.title){
44393             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44394         }
44395     },
44396
44397     setActivePanel : function(panel)
44398     {
44399         panel = this.getPanel(panel);
44400         if(this.activePanel && this.activePanel != panel){
44401             if(this.activePanel.setActiveState(false) === false){
44402                 return;
44403             }
44404         }
44405         this.activePanel = panel;
44406         panel.setActiveState(true);
44407         if(this.panelSize){
44408             panel.setSize(this.panelSize.width, this.panelSize.height);
44409         }
44410         if(this.closeBtn){
44411             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44412         }
44413         this.updateTitle(panel.getTitle());
44414         if(this.tabs){
44415             this.fireEvent("invalidated", this);
44416         }
44417         this.fireEvent("panelactivated", this, panel);
44418     },
44419
44420     /**
44421      * Shows the specified panel.
44422      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44423      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44424      */
44425     showPanel : function(panel)
44426     {
44427         panel = this.getPanel(panel);
44428         if(panel){
44429             if(this.tabs){
44430                 var tab = this.tabs.getTab(panel.getEl().id);
44431                 if(tab.isHidden()){
44432                     this.tabs.unhideTab(tab.id);
44433                 }
44434                 tab.activate();
44435             }else{
44436                 this.setActivePanel(panel);
44437             }
44438         }
44439         return panel;
44440     },
44441
44442     /**
44443      * Get the active panel for this region.
44444      * @return {Roo.ContentPanel} The active panel or null
44445      */
44446     getActivePanel : function(){
44447         return this.activePanel;
44448     },
44449
44450     validateVisibility : function(){
44451         if(this.panels.getCount() < 1){
44452             this.updateTitle("&#160;");
44453             this.closeBtn.hide();
44454             this.hide();
44455         }else{
44456             if(!this.isVisible()){
44457                 this.show();
44458             }
44459         }
44460     },
44461
44462     /**
44463      * Adds the passed ContentPanel(s) to this region.
44464      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44465      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44466      */
44467     add : function(panel)
44468     {
44469         if(arguments.length > 1){
44470             for(var i = 0, len = arguments.length; i < len; i++) {
44471                 this.add(arguments[i]);
44472             }
44473             return null;
44474         }
44475         
44476         // if we have not been rendered yet, then we can not really do much of this..
44477         if (!this.bodyEl) {
44478             this.unrendered_panels.push(panel);
44479             return panel;
44480         }
44481         
44482         
44483         
44484         
44485         if(this.hasPanel(panel)){
44486             this.showPanel(panel);
44487             return panel;
44488         }
44489         panel.setRegion(this);
44490         this.panels.add(panel);
44491        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44492             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44493             // and hide them... ???
44494             this.bodyEl.dom.appendChild(panel.getEl().dom);
44495             if(panel.background !== true){
44496                 this.setActivePanel(panel);
44497             }
44498             this.fireEvent("paneladded", this, panel);
44499             return panel;
44500         }
44501         */
44502         if(!this.tabs){
44503             this.initTabs();
44504         }else{
44505             this.initPanelAsTab(panel);
44506         }
44507         
44508         
44509         if(panel.background !== true){
44510             this.tabs.activate(panel.getEl().id);
44511         }
44512         this.fireEvent("paneladded", this, panel);
44513         return panel;
44514     },
44515
44516     /**
44517      * Hides the tab for the specified panel.
44518      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44519      */
44520     hidePanel : function(panel){
44521         if(this.tabs && (panel = this.getPanel(panel))){
44522             this.tabs.hideTab(panel.getEl().id);
44523         }
44524     },
44525
44526     /**
44527      * Unhides the tab for a previously hidden panel.
44528      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44529      */
44530     unhidePanel : function(panel){
44531         if(this.tabs && (panel = this.getPanel(panel))){
44532             this.tabs.unhideTab(panel.getEl().id);
44533         }
44534     },
44535
44536     clearPanels : function(){
44537         while(this.panels.getCount() > 0){
44538              this.remove(this.panels.first());
44539         }
44540     },
44541
44542     /**
44543      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44544      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44545      * @param {Boolean} preservePanel Overrides the config preservePanel option
44546      * @return {Roo.ContentPanel} The panel that was removed
44547      */
44548     remove : function(panel, preservePanel)
44549     {
44550         panel = this.getPanel(panel);
44551         if(!panel){
44552             return null;
44553         }
44554         var e = {};
44555         this.fireEvent("beforeremove", this, panel, e);
44556         if(e.cancel === true){
44557             return null;
44558         }
44559         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44560         var panelId = panel.getId();
44561         this.panels.removeKey(panelId);
44562         if(preservePanel){
44563             document.body.appendChild(panel.getEl().dom);
44564         }
44565         if(this.tabs){
44566             this.tabs.removeTab(panel.getEl().id);
44567         }else if (!preservePanel){
44568             this.bodyEl.dom.removeChild(panel.getEl().dom);
44569         }
44570         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44571             var p = this.panels.first();
44572             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44573             tempEl.appendChild(p.getEl().dom);
44574             this.bodyEl.update("");
44575             this.bodyEl.dom.appendChild(p.getEl().dom);
44576             tempEl = null;
44577             this.updateTitle(p.getTitle());
44578             this.tabs = null;
44579             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44580             this.setActivePanel(p);
44581         }
44582         panel.setRegion(null);
44583         if(this.activePanel == panel){
44584             this.activePanel = null;
44585         }
44586         if(this.config.autoDestroy !== false && preservePanel !== true){
44587             try{panel.destroy();}catch(e){}
44588         }
44589         this.fireEvent("panelremoved", this, panel);
44590         return panel;
44591     },
44592
44593     /**
44594      * Returns the TabPanel component used by this region
44595      * @return {Roo.TabPanel}
44596      */
44597     getTabs : function(){
44598         return this.tabs;
44599     },
44600
44601     createTool : function(parentEl, className){
44602         var btn = Roo.DomHelper.append(parentEl, {
44603             tag: "div",
44604             cls: "x-layout-tools-button",
44605             children: [ {
44606                 tag: "div",
44607                 cls: "roo-layout-tools-button-inner " + className,
44608                 html: "&#160;"
44609             }]
44610         }, true);
44611         btn.addClassOnOver("roo-layout-tools-button-over");
44612         return btn;
44613     }
44614 });/*
44615  * Based on:
44616  * Ext JS Library 1.1.1
44617  * Copyright(c) 2006-2007, Ext JS, LLC.
44618  *
44619  * Originally Released Under LGPL - original licence link has changed is not relivant.
44620  *
44621  * Fork - LGPL
44622  * <script type="text/javascript">
44623  */
44624  
44625
44626
44627 /**
44628  * @class Roo.SplitLayoutRegion
44629  * @extends Roo.LayoutRegion
44630  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44631  */
44632 Roo.bootstrap.layout.Split = function(config){
44633     this.cursor = config.cursor;
44634     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44635 };
44636
44637 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44638 {
44639     splitTip : "Drag to resize.",
44640     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44641     useSplitTips : false,
44642
44643     applyConfig : function(config){
44644         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44645     },
44646     
44647     onRender : function(ctr,pos) {
44648         
44649         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44650         if(!this.config.split){
44651             return;
44652         }
44653         if(!this.split){
44654             
44655             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44656                             tag: "div",
44657                             id: this.el.id + "-split",
44658                             cls: "roo-layout-split roo-layout-split-"+this.position,
44659                             html: "&#160;"
44660             });
44661             /** The SplitBar for this region 
44662             * @type Roo.SplitBar */
44663             // does not exist yet...
44664             Roo.log([this.position, this.orientation]);
44665             
44666             this.split = new Roo.bootstrap.SplitBar({
44667                 dragElement : splitEl,
44668                 resizingElement: this.el,
44669                 orientation : this.orientation
44670             });
44671             
44672             this.split.on("moved", this.onSplitMove, this);
44673             this.split.useShim = this.config.useShim === true;
44674             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44675             if(this.useSplitTips){
44676                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44677             }
44678             //if(config.collapsible){
44679             //    this.split.el.on("dblclick", this.collapse,  this);
44680             //}
44681         }
44682         if(typeof this.config.minSize != "undefined"){
44683             this.split.minSize = this.config.minSize;
44684         }
44685         if(typeof this.config.maxSize != "undefined"){
44686             this.split.maxSize = this.config.maxSize;
44687         }
44688         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44689             this.hideSplitter();
44690         }
44691         
44692     },
44693
44694     getHMaxSize : function(){
44695          var cmax = this.config.maxSize || 10000;
44696          var center = this.mgr.getRegion("center");
44697          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44698     },
44699
44700     getVMaxSize : function(){
44701          var cmax = this.config.maxSize || 10000;
44702          var center = this.mgr.getRegion("center");
44703          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44704     },
44705
44706     onSplitMove : function(split, newSize){
44707         this.fireEvent("resized", this, newSize);
44708     },
44709     
44710     /** 
44711      * Returns the {@link Roo.SplitBar} for this region.
44712      * @return {Roo.SplitBar}
44713      */
44714     getSplitBar : function(){
44715         return this.split;
44716     },
44717     
44718     hide : function(){
44719         this.hideSplitter();
44720         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44721     },
44722
44723     hideSplitter : function(){
44724         if(this.split){
44725             this.split.el.setLocation(-2000,-2000);
44726             this.split.el.hide();
44727         }
44728     },
44729
44730     show : function(){
44731         if(this.split){
44732             this.split.el.show();
44733         }
44734         Roo.bootstrap.layout.Split.superclass.show.call(this);
44735     },
44736     
44737     beforeSlide: function(){
44738         if(Roo.isGecko){// firefox overflow auto bug workaround
44739             this.bodyEl.clip();
44740             if(this.tabs) {
44741                 this.tabs.bodyEl.clip();
44742             }
44743             if(this.activePanel){
44744                 this.activePanel.getEl().clip();
44745                 
44746                 if(this.activePanel.beforeSlide){
44747                     this.activePanel.beforeSlide();
44748                 }
44749             }
44750         }
44751     },
44752     
44753     afterSlide : function(){
44754         if(Roo.isGecko){// firefox overflow auto bug workaround
44755             this.bodyEl.unclip();
44756             if(this.tabs) {
44757                 this.tabs.bodyEl.unclip();
44758             }
44759             if(this.activePanel){
44760                 this.activePanel.getEl().unclip();
44761                 if(this.activePanel.afterSlide){
44762                     this.activePanel.afterSlide();
44763                 }
44764             }
44765         }
44766     },
44767
44768     initAutoHide : function(){
44769         if(this.autoHide !== false){
44770             if(!this.autoHideHd){
44771                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44772                 this.autoHideHd = {
44773                     "mouseout": function(e){
44774                         if(!e.within(this.el, true)){
44775                             st.delay(500);
44776                         }
44777                     },
44778                     "mouseover" : function(e){
44779                         st.cancel();
44780                     },
44781                     scope : this
44782                 };
44783             }
44784             this.el.on(this.autoHideHd);
44785         }
44786     },
44787
44788     clearAutoHide : function(){
44789         if(this.autoHide !== false){
44790             this.el.un("mouseout", this.autoHideHd.mouseout);
44791             this.el.un("mouseover", this.autoHideHd.mouseover);
44792         }
44793     },
44794
44795     clearMonitor : function(){
44796         Roo.get(document).un("click", this.slideInIf, this);
44797     },
44798
44799     // these names are backwards but not changed for compat
44800     slideOut : function(){
44801         if(this.isSlid || this.el.hasActiveFx()){
44802             return;
44803         }
44804         this.isSlid = true;
44805         if(this.collapseBtn){
44806             this.collapseBtn.hide();
44807         }
44808         this.closeBtnState = this.closeBtn.getStyle('display');
44809         this.closeBtn.hide();
44810         if(this.stickBtn){
44811             this.stickBtn.show();
44812         }
44813         this.el.show();
44814         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44815         this.beforeSlide();
44816         this.el.setStyle("z-index", 10001);
44817         this.el.slideIn(this.getSlideAnchor(), {
44818             callback: function(){
44819                 this.afterSlide();
44820                 this.initAutoHide();
44821                 Roo.get(document).on("click", this.slideInIf, this);
44822                 this.fireEvent("slideshow", this);
44823             },
44824             scope: this,
44825             block: true
44826         });
44827     },
44828
44829     afterSlideIn : function(){
44830         this.clearAutoHide();
44831         this.isSlid = false;
44832         this.clearMonitor();
44833         this.el.setStyle("z-index", "");
44834         if(this.collapseBtn){
44835             this.collapseBtn.show();
44836         }
44837         this.closeBtn.setStyle('display', this.closeBtnState);
44838         if(this.stickBtn){
44839             this.stickBtn.hide();
44840         }
44841         this.fireEvent("slidehide", this);
44842     },
44843
44844     slideIn : function(cb){
44845         if(!this.isSlid || this.el.hasActiveFx()){
44846             Roo.callback(cb);
44847             return;
44848         }
44849         this.isSlid = false;
44850         this.beforeSlide();
44851         this.el.slideOut(this.getSlideAnchor(), {
44852             callback: function(){
44853                 this.el.setLeftTop(-10000, -10000);
44854                 this.afterSlide();
44855                 this.afterSlideIn();
44856                 Roo.callback(cb);
44857             },
44858             scope: this,
44859             block: true
44860         });
44861     },
44862     
44863     slideInIf : function(e){
44864         if(!e.within(this.el)){
44865             this.slideIn();
44866         }
44867     },
44868
44869     animateCollapse : function(){
44870         this.beforeSlide();
44871         this.el.setStyle("z-index", 20000);
44872         var anchor = this.getSlideAnchor();
44873         this.el.slideOut(anchor, {
44874             callback : function(){
44875                 this.el.setStyle("z-index", "");
44876                 this.collapsedEl.slideIn(anchor, {duration:.3});
44877                 this.afterSlide();
44878                 this.el.setLocation(-10000,-10000);
44879                 this.el.hide();
44880                 this.fireEvent("collapsed", this);
44881             },
44882             scope: this,
44883             block: true
44884         });
44885     },
44886
44887     animateExpand : function(){
44888         this.beforeSlide();
44889         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44890         this.el.setStyle("z-index", 20000);
44891         this.collapsedEl.hide({
44892             duration:.1
44893         });
44894         this.el.slideIn(this.getSlideAnchor(), {
44895             callback : function(){
44896                 this.el.setStyle("z-index", "");
44897                 this.afterSlide();
44898                 if(this.split){
44899                     this.split.el.show();
44900                 }
44901                 this.fireEvent("invalidated", this);
44902                 this.fireEvent("expanded", this);
44903             },
44904             scope: this,
44905             block: true
44906         });
44907     },
44908
44909     anchors : {
44910         "west" : "left",
44911         "east" : "right",
44912         "north" : "top",
44913         "south" : "bottom"
44914     },
44915
44916     sanchors : {
44917         "west" : "l",
44918         "east" : "r",
44919         "north" : "t",
44920         "south" : "b"
44921     },
44922
44923     canchors : {
44924         "west" : "tl-tr",
44925         "east" : "tr-tl",
44926         "north" : "tl-bl",
44927         "south" : "bl-tl"
44928     },
44929
44930     getAnchor : function(){
44931         return this.anchors[this.position];
44932     },
44933
44934     getCollapseAnchor : function(){
44935         return this.canchors[this.position];
44936     },
44937
44938     getSlideAnchor : function(){
44939         return this.sanchors[this.position];
44940     },
44941
44942     getAlignAdj : function(){
44943         var cm = this.cmargins;
44944         switch(this.position){
44945             case "west":
44946                 return [0, 0];
44947             break;
44948             case "east":
44949                 return [0, 0];
44950             break;
44951             case "north":
44952                 return [0, 0];
44953             break;
44954             case "south":
44955                 return [0, 0];
44956             break;
44957         }
44958     },
44959
44960     getExpandAdj : function(){
44961         var c = this.collapsedEl, cm = this.cmargins;
44962         switch(this.position){
44963             case "west":
44964                 return [-(cm.right+c.getWidth()+cm.left), 0];
44965             break;
44966             case "east":
44967                 return [cm.right+c.getWidth()+cm.left, 0];
44968             break;
44969             case "north":
44970                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44971             break;
44972             case "south":
44973                 return [0, cm.top+cm.bottom+c.getHeight()];
44974             break;
44975         }
44976     }
44977 });/*
44978  * Based on:
44979  * Ext JS Library 1.1.1
44980  * Copyright(c) 2006-2007, Ext JS, LLC.
44981  *
44982  * Originally Released Under LGPL - original licence link has changed is not relivant.
44983  *
44984  * Fork - LGPL
44985  * <script type="text/javascript">
44986  */
44987 /*
44988  * These classes are private internal classes
44989  */
44990 Roo.bootstrap.layout.Center = function(config){
44991     config.region = "center";
44992     Roo.bootstrap.layout.Region.call(this, config);
44993     this.visible = true;
44994     this.minWidth = config.minWidth || 20;
44995     this.minHeight = config.minHeight || 20;
44996 };
44997
44998 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44999     hide : function(){
45000         // center panel can't be hidden
45001     },
45002     
45003     show : function(){
45004         // center panel can't be hidden
45005     },
45006     
45007     getMinWidth: function(){
45008         return this.minWidth;
45009     },
45010     
45011     getMinHeight: function(){
45012         return this.minHeight;
45013     }
45014 });
45015
45016
45017
45018
45019  
45020
45021
45022
45023
45024
45025
45026 Roo.bootstrap.layout.North = function(config)
45027 {
45028     config.region = 'north';
45029     config.cursor = 'n-resize';
45030     
45031     Roo.bootstrap.layout.Split.call(this, config);
45032     
45033     
45034     if(this.split){
45035         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45036         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45037         this.split.el.addClass("roo-layout-split-v");
45038     }
45039     //var size = config.initialSize || config.height;
45040     //if(this.el && typeof size != "undefined"){
45041     //    this.el.setHeight(size);
45042     //}
45043 };
45044 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45045 {
45046     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45047      
45048      
45049     onRender : function(ctr, pos)
45050     {
45051         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45052         var size = this.config.initialSize || this.config.height;
45053         if(this.el && typeof size != "undefined"){
45054             this.el.setHeight(size);
45055         }
45056     
45057     },
45058     
45059     getBox : function(){
45060         if(this.collapsed){
45061             return this.collapsedEl.getBox();
45062         }
45063         var box = this.el.getBox();
45064         if(this.split){
45065             box.height += this.split.el.getHeight();
45066         }
45067         return box;
45068     },
45069     
45070     updateBox : function(box){
45071         if(this.split && !this.collapsed){
45072             box.height -= this.split.el.getHeight();
45073             this.split.el.setLeft(box.x);
45074             this.split.el.setTop(box.y+box.height);
45075             this.split.el.setWidth(box.width);
45076         }
45077         if(this.collapsed){
45078             this.updateBody(box.width, null);
45079         }
45080         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45081     }
45082 });
45083
45084
45085
45086
45087
45088 Roo.bootstrap.layout.South = function(config){
45089     config.region = 'south';
45090     config.cursor = 's-resize';
45091     Roo.bootstrap.layout.Split.call(this, config);
45092     if(this.split){
45093         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45094         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45095         this.split.el.addClass("roo-layout-split-v");
45096     }
45097     
45098 };
45099
45100 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45101     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45102     
45103     onRender : function(ctr, pos)
45104     {
45105         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45106         var size = this.config.initialSize || this.config.height;
45107         if(this.el && typeof size != "undefined"){
45108             this.el.setHeight(size);
45109         }
45110     
45111     },
45112     
45113     getBox : function(){
45114         if(this.collapsed){
45115             return this.collapsedEl.getBox();
45116         }
45117         var box = this.el.getBox();
45118         if(this.split){
45119             var sh = this.split.el.getHeight();
45120             box.height += sh;
45121             box.y -= sh;
45122         }
45123         return box;
45124     },
45125     
45126     updateBox : function(box){
45127         if(this.split && !this.collapsed){
45128             var sh = this.split.el.getHeight();
45129             box.height -= sh;
45130             box.y += sh;
45131             this.split.el.setLeft(box.x);
45132             this.split.el.setTop(box.y-sh);
45133             this.split.el.setWidth(box.width);
45134         }
45135         if(this.collapsed){
45136             this.updateBody(box.width, null);
45137         }
45138         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45139     }
45140 });
45141
45142 Roo.bootstrap.layout.East = function(config){
45143     config.region = "east";
45144     config.cursor = "e-resize";
45145     Roo.bootstrap.layout.Split.call(this, config);
45146     if(this.split){
45147         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45148         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45149         this.split.el.addClass("roo-layout-split-h");
45150     }
45151     
45152 };
45153 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45154     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45155     
45156     onRender : function(ctr, pos)
45157     {
45158         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45159         var size = this.config.initialSize || this.config.width;
45160         if(this.el && typeof size != "undefined"){
45161             this.el.setWidth(size);
45162         }
45163     
45164     },
45165     
45166     getBox : function(){
45167         if(this.collapsed){
45168             return this.collapsedEl.getBox();
45169         }
45170         var box = this.el.getBox();
45171         if(this.split){
45172             var sw = this.split.el.getWidth();
45173             box.width += sw;
45174             box.x -= sw;
45175         }
45176         return box;
45177     },
45178
45179     updateBox : function(box){
45180         if(this.split && !this.collapsed){
45181             var sw = this.split.el.getWidth();
45182             box.width -= sw;
45183             this.split.el.setLeft(box.x);
45184             this.split.el.setTop(box.y);
45185             this.split.el.setHeight(box.height);
45186             box.x += sw;
45187         }
45188         if(this.collapsed){
45189             this.updateBody(null, box.height);
45190         }
45191         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45192     }
45193 });
45194
45195 Roo.bootstrap.layout.West = function(config){
45196     config.region = "west";
45197     config.cursor = "w-resize";
45198     
45199     Roo.bootstrap.layout.Split.call(this, config);
45200     if(this.split){
45201         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45202         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45203         this.split.el.addClass("roo-layout-split-h");
45204     }
45205     
45206 };
45207 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45208     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45209     
45210     onRender: function(ctr, pos)
45211     {
45212         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45213         var size = this.config.initialSize || this.config.width;
45214         if(typeof size != "undefined"){
45215             this.el.setWidth(size);
45216         }
45217     },
45218     
45219     getBox : function(){
45220         if(this.collapsed){
45221             return this.collapsedEl.getBox();
45222         }
45223         var box = this.el.getBox();
45224         if (box.width == 0) {
45225             box.width = this.config.width; // kludge?
45226         }
45227         if(this.split){
45228             box.width += this.split.el.getWidth();
45229         }
45230         return box;
45231     },
45232     
45233     updateBox : function(box){
45234         if(this.split && !this.collapsed){
45235             var sw = this.split.el.getWidth();
45236             box.width -= sw;
45237             this.split.el.setLeft(box.x+box.width);
45238             this.split.el.setTop(box.y);
45239             this.split.el.setHeight(box.height);
45240         }
45241         if(this.collapsed){
45242             this.updateBody(null, box.height);
45243         }
45244         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45245     }
45246 });/*
45247  * Based on:
45248  * Ext JS Library 1.1.1
45249  * Copyright(c) 2006-2007, Ext JS, LLC.
45250  *
45251  * Originally Released Under LGPL - original licence link has changed is not relivant.
45252  *
45253  * Fork - LGPL
45254  * <script type="text/javascript">
45255  */
45256 /**
45257  * @class Roo.bootstrap.paenl.Content
45258  * @extends Roo.util.Observable
45259  * @children Roo.bootstrap.Component
45260  * @parent builder Roo.bootstrap.layout.Border
45261  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45262  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45263  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45264  * @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
45265  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45266  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45267  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45268  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45269  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45270  * @cfg {String} title          The title for this panel
45271  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45272  * @cfg {String} url            Calls {@link #setUrl} with this value
45273  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45274  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45275  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45276  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45277  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45278  * @cfg {Boolean} badges render the badges
45279  * @cfg {String} cls  extra classes to use  
45280  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45281  
45282  * @constructor
45283  * Create a new ContentPanel.
45284  * @param {String/Object} config A string to set only the title or a config object
45285  
45286  */
45287 Roo.bootstrap.panel.Content = function( config){
45288     
45289     this.tpl = config.tpl || false;
45290     
45291     var el = config.el;
45292     var content = config.content;
45293
45294     if(config.autoCreate){ // xtype is available if this is called from factory
45295         el = Roo.id();
45296     }
45297     this.el = Roo.get(el);
45298     if(!this.el && config && config.autoCreate){
45299         if(typeof config.autoCreate == "object"){
45300             if(!config.autoCreate.id){
45301                 config.autoCreate.id = config.id||el;
45302             }
45303             this.el = Roo.DomHelper.append(document.body,
45304                         config.autoCreate, true);
45305         }else{
45306             var elcfg =  {
45307                 tag: "div",
45308                 cls: (config.cls || '') +
45309                     (config.background ? ' bg-' + config.background : '') +
45310                     " roo-layout-inactive-content",
45311                 id: config.id||el
45312             };
45313             if (config.iframe) {
45314                 elcfg.cn = [
45315                     {
45316                         tag : 'iframe',
45317                         style : 'border: 0px',
45318                         src : 'about:blank'
45319                     }
45320                 ];
45321             }
45322               
45323             if (config.html) {
45324                 elcfg.html = config.html;
45325                 
45326             }
45327                         
45328             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45329             if (config.iframe) {
45330                 this.iframeEl = this.el.select('iframe',true).first();
45331             }
45332             
45333         }
45334     } 
45335     this.closable = false;
45336     this.loaded = false;
45337     this.active = false;
45338    
45339       
45340     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45341         
45342         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45343         
45344         this.wrapEl = this.el; //this.el.wrap();
45345         var ti = [];
45346         if (config.toolbar.items) {
45347             ti = config.toolbar.items ;
45348             delete config.toolbar.items ;
45349         }
45350         
45351         var nitems = [];
45352         this.toolbar.render(this.wrapEl, 'before');
45353         for(var i =0;i < ti.length;i++) {
45354           //  Roo.log(['add child', items[i]]);
45355             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45356         }
45357         this.toolbar.items = nitems;
45358         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45359         delete config.toolbar;
45360         
45361     }
45362     /*
45363     // xtype created footer. - not sure if will work as we normally have to render first..
45364     if (this.footer && !this.footer.el && this.footer.xtype) {
45365         if (!this.wrapEl) {
45366             this.wrapEl = this.el.wrap();
45367         }
45368     
45369         this.footer.container = this.wrapEl.createChild();
45370          
45371         this.footer = Roo.factory(this.footer, Roo);
45372         
45373     }
45374     */
45375     
45376      if(typeof config == "string"){
45377         this.title = config;
45378     }else{
45379         Roo.apply(this, config);
45380     }
45381     
45382     if(this.resizeEl){
45383         this.resizeEl = Roo.get(this.resizeEl, true);
45384     }else{
45385         this.resizeEl = this.el;
45386     }
45387     // handle view.xtype
45388     
45389  
45390     
45391     
45392     this.addEvents({
45393         /**
45394          * @event activate
45395          * Fires when this panel is activated. 
45396          * @param {Roo.ContentPanel} this
45397          */
45398         "activate" : true,
45399         /**
45400          * @event deactivate
45401          * Fires when this panel is activated. 
45402          * @param {Roo.ContentPanel} this
45403          */
45404         "deactivate" : true,
45405
45406         /**
45407          * @event resize
45408          * Fires when this panel is resized if fitToFrame is true.
45409          * @param {Roo.ContentPanel} this
45410          * @param {Number} width The width after any component adjustments
45411          * @param {Number} height The height after any component adjustments
45412          */
45413         "resize" : true,
45414         
45415          /**
45416          * @event render
45417          * Fires when this tab is created
45418          * @param {Roo.ContentPanel} this
45419          */
45420         "render" : true,
45421         
45422           /**
45423          * @event scroll
45424          * Fires when this content is scrolled
45425          * @param {Roo.ContentPanel} this
45426          * @param {Event} scrollEvent
45427          */
45428         "scroll" : true
45429         
45430         
45431         
45432     });
45433     
45434
45435     
45436     
45437     if(this.autoScroll && !this.iframe){
45438         this.resizeEl.setStyle("overflow", "auto");
45439         this.resizeEl.on('scroll', this.onScroll, this);
45440     } else {
45441         // fix randome scrolling
45442         //this.el.on('scroll', function() {
45443         //    Roo.log('fix random scolling');
45444         //    this.scrollTo('top',0); 
45445         //});
45446     }
45447     content = content || this.content;
45448     if(content){
45449         this.setContent(content);
45450     }
45451     if(config && config.url){
45452         this.setUrl(this.url, this.params, this.loadOnce);
45453     }
45454     
45455     
45456     
45457     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45458     
45459     if (this.view && typeof(this.view.xtype) != 'undefined') {
45460         this.view.el = this.el.appendChild(document.createElement("div"));
45461         this.view = Roo.factory(this.view); 
45462         this.view.render  &&  this.view.render(false, '');  
45463     }
45464     
45465     
45466     this.fireEvent('render', this);
45467 };
45468
45469 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45470     
45471     cls : '',
45472     background : '',
45473     
45474     tabTip : '',
45475     
45476     iframe : false,
45477     iframeEl : false,
45478     
45479     /* Resize Element - use this to work out scroll etc. */
45480     resizeEl : false,
45481     
45482     setRegion : function(region){
45483         this.region = region;
45484         this.setActiveClass(region && !this.background);
45485     },
45486     
45487     
45488     setActiveClass: function(state)
45489     {
45490         if(state){
45491            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45492            this.el.setStyle('position','relative');
45493         }else{
45494            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45495            this.el.setStyle('position', 'absolute');
45496         } 
45497     },
45498     
45499     /**
45500      * Returns the toolbar for this Panel if one was configured. 
45501      * @return {Roo.Toolbar} 
45502      */
45503     getToolbar : function(){
45504         return this.toolbar;
45505     },
45506     
45507     setActiveState : function(active)
45508     {
45509         this.active = active;
45510         this.setActiveClass(active);
45511         if(!active){
45512             if(this.fireEvent("deactivate", this) === false){
45513                 return false;
45514             }
45515             return true;
45516         }
45517         this.fireEvent("activate", this);
45518         return true;
45519     },
45520     /**
45521      * Updates this panel's element (not for iframe)
45522      * @param {String} content The new content
45523      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45524     */
45525     setContent : function(content, loadScripts){
45526         if (this.iframe) {
45527             return;
45528         }
45529         
45530         this.el.update(content, loadScripts);
45531     },
45532
45533     ignoreResize : function(w, h)
45534     {
45535         //return false; // always resize?
45536         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45537             return true;
45538         }else{
45539             this.lastSize = {width: w, height: h};
45540             return false;
45541         }
45542     },
45543     /**
45544      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45545      * @return {Roo.UpdateManager} The UpdateManager
45546      */
45547     getUpdateManager : function(){
45548         if (this.iframe) {
45549             return false;
45550         }
45551         return this.el.getUpdateManager();
45552     },
45553      /**
45554      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45555      * Does not work with IFRAME contents
45556      * @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:
45557 <pre><code>
45558 panel.load({
45559     url: "your-url.php",
45560     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45561     callback: yourFunction,
45562     scope: yourObject, //(optional scope)
45563     discardUrl: false,
45564     nocache: false,
45565     text: "Loading...",
45566     timeout: 30,
45567     scripts: false
45568 });
45569 </code></pre>
45570      
45571      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45572      * 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.
45573      * @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}
45574      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45575      * @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.
45576      * @return {Roo.ContentPanel} this
45577      */
45578     load : function(){
45579         
45580         if (this.iframe) {
45581             return this;
45582         }
45583         
45584         var um = this.el.getUpdateManager();
45585         um.update.apply(um, arguments);
45586         return this;
45587     },
45588
45589
45590     /**
45591      * 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.
45592      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45593      * @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)
45594      * @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)
45595      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45596      */
45597     setUrl : function(url, params, loadOnce){
45598         if (this.iframe) {
45599             this.iframeEl.dom.src = url;
45600             return false;
45601         }
45602         
45603         if(this.refreshDelegate){
45604             this.removeListener("activate", this.refreshDelegate);
45605         }
45606         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45607         this.on("activate", this.refreshDelegate);
45608         return this.el.getUpdateManager();
45609     },
45610     
45611     _handleRefresh : function(url, params, loadOnce){
45612         if(!loadOnce || !this.loaded){
45613             var updater = this.el.getUpdateManager();
45614             updater.update(url, params, this._setLoaded.createDelegate(this));
45615         }
45616     },
45617     
45618     _setLoaded : function(){
45619         this.loaded = true;
45620     }, 
45621     
45622     /**
45623      * Returns this panel's id
45624      * @return {String} 
45625      */
45626     getId : function(){
45627         return this.el.id;
45628     },
45629     
45630     /** 
45631      * Returns this panel's element - used by regiosn to add.
45632      * @return {Roo.Element} 
45633      */
45634     getEl : function(){
45635         return this.wrapEl || this.el;
45636     },
45637     
45638    
45639     
45640     adjustForComponents : function(width, height)
45641     {
45642         //Roo.log('adjustForComponents ');
45643         if(this.resizeEl != this.el){
45644             width -= this.el.getFrameWidth('lr');
45645             height -= this.el.getFrameWidth('tb');
45646         }
45647         if(this.toolbar){
45648             var te = this.toolbar.getEl();
45649             te.setWidth(width);
45650             height -= te.getHeight();
45651         }
45652         if(this.footer){
45653             var te = this.footer.getEl();
45654             te.setWidth(width);
45655             height -= te.getHeight();
45656         }
45657         
45658         
45659         if(this.adjustments){
45660             width += this.adjustments[0];
45661             height += this.adjustments[1];
45662         }
45663         return {"width": width, "height": height};
45664     },
45665     
45666     setSize : function(width, height){
45667         if(this.fitToFrame && !this.ignoreResize(width, height)){
45668             if(this.fitContainer && this.resizeEl != this.el){
45669                 this.el.setSize(width, height);
45670             }
45671             var size = this.adjustForComponents(width, height);
45672             if (this.iframe) {
45673                 this.iframeEl.setSize(width,height);
45674             }
45675             
45676             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45677             this.fireEvent('resize', this, size.width, size.height);
45678             
45679             
45680         }
45681     },
45682     
45683     /**
45684      * Returns this panel's title
45685      * @return {String} 
45686      */
45687     getTitle : function(){
45688         
45689         if (typeof(this.title) != 'object') {
45690             return this.title;
45691         }
45692         
45693         var t = '';
45694         for (var k in this.title) {
45695             if (!this.title.hasOwnProperty(k)) {
45696                 continue;
45697             }
45698             
45699             if (k.indexOf('-') >= 0) {
45700                 var s = k.split('-');
45701                 for (var i = 0; i<s.length; i++) {
45702                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45703                 }
45704             } else {
45705                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45706             }
45707         }
45708         return t;
45709     },
45710     
45711     /**
45712      * Set this panel's title
45713      * @param {String} title
45714      */
45715     setTitle : function(title){
45716         this.title = title;
45717         if(this.region){
45718             this.region.updatePanelTitle(this, title);
45719         }
45720     },
45721     
45722     /**
45723      * Returns true is this panel was configured to be closable
45724      * @return {Boolean} 
45725      */
45726     isClosable : function(){
45727         return this.closable;
45728     },
45729     
45730     beforeSlide : function(){
45731         this.el.clip();
45732         this.resizeEl.clip();
45733     },
45734     
45735     afterSlide : function(){
45736         this.el.unclip();
45737         this.resizeEl.unclip();
45738     },
45739     
45740     /**
45741      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45742      *   Will fail silently if the {@link #setUrl} method has not been called.
45743      *   This does not activate the panel, just updates its content.
45744      */
45745     refresh : function(){
45746         if(this.refreshDelegate){
45747            this.loaded = false;
45748            this.refreshDelegate();
45749         }
45750     },
45751     
45752     /**
45753      * Destroys this panel
45754      */
45755     destroy : function(){
45756         this.el.removeAllListeners();
45757         var tempEl = document.createElement("span");
45758         tempEl.appendChild(this.el.dom);
45759         tempEl.innerHTML = "";
45760         this.el.remove();
45761         this.el = null;
45762     },
45763     
45764     /**
45765      * form - if the content panel contains a form - this is a reference to it.
45766      * @type {Roo.form.Form}
45767      */
45768     form : false,
45769     /**
45770      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45771      *    This contains a reference to it.
45772      * @type {Roo.View}
45773      */
45774     view : false,
45775     
45776       /**
45777      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45778      * <pre><code>
45779
45780 layout.addxtype({
45781        xtype : 'Form',
45782        items: [ .... ]
45783    }
45784 );
45785
45786 </code></pre>
45787      * @param {Object} cfg Xtype definition of item to add.
45788      */
45789     
45790     
45791     getChildContainer: function () {
45792         return this.getEl();
45793     },
45794     
45795     
45796     onScroll : function(e)
45797     {
45798         this.fireEvent('scroll', this, e);
45799     }
45800     
45801     
45802     /*
45803         var  ret = new Roo.factory(cfg);
45804         return ret;
45805         
45806         
45807         // add form..
45808         if (cfg.xtype.match(/^Form$/)) {
45809             
45810             var el;
45811             //if (this.footer) {
45812             //    el = this.footer.container.insertSibling(false, 'before');
45813             //} else {
45814                 el = this.el.createChild();
45815             //}
45816
45817             this.form = new  Roo.form.Form(cfg);
45818             
45819             
45820             if ( this.form.allItems.length) {
45821                 this.form.render(el.dom);
45822             }
45823             return this.form;
45824         }
45825         // should only have one of theses..
45826         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45827             // views.. should not be just added - used named prop 'view''
45828             
45829             cfg.el = this.el.appendChild(document.createElement("div"));
45830             // factory?
45831             
45832             var ret = new Roo.factory(cfg);
45833              
45834              ret.render && ret.render(false, ''); // render blank..
45835             this.view = ret;
45836             return ret;
45837         }
45838         return false;
45839     }
45840     \*/
45841 });
45842  
45843 /**
45844  * @class Roo.bootstrap.panel.Grid
45845  * @extends Roo.bootstrap.panel.Content
45846  * @constructor
45847  * Create a new GridPanel.
45848  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45849  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45850  * @param {Object} config A the config object
45851   
45852  */
45853
45854
45855
45856 Roo.bootstrap.panel.Grid = function(config)
45857 {
45858     
45859       
45860     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45861         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45862
45863     config.el = this.wrapper;
45864     //this.el = this.wrapper;
45865     
45866       if (config.container) {
45867         // ctor'ed from a Border/panel.grid
45868         
45869         
45870         this.wrapper.setStyle("overflow", "hidden");
45871         this.wrapper.addClass('roo-grid-container');
45872
45873     }
45874     
45875     
45876     if(config.toolbar){
45877         var tool_el = this.wrapper.createChild();    
45878         this.toolbar = Roo.factory(config.toolbar);
45879         var ti = [];
45880         if (config.toolbar.items) {
45881             ti = config.toolbar.items ;
45882             delete config.toolbar.items ;
45883         }
45884         
45885         var nitems = [];
45886         this.toolbar.render(tool_el);
45887         for(var i =0;i < ti.length;i++) {
45888           //  Roo.log(['add child', items[i]]);
45889             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45890         }
45891         this.toolbar.items = nitems;
45892         
45893         delete config.toolbar;
45894     }
45895     
45896     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45897     config.grid.scrollBody = true;;
45898     config.grid.monitorWindowResize = false; // turn off autosizing
45899     config.grid.autoHeight = false;
45900     config.grid.autoWidth = false;
45901     
45902     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45903     
45904     if (config.background) {
45905         // render grid on panel activation (if panel background)
45906         this.on('activate', function(gp) {
45907             if (!gp.grid.rendered) {
45908                 gp.grid.render(this.wrapper);
45909                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45910             }
45911         });
45912             
45913     } else {
45914         this.grid.render(this.wrapper);
45915         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45916
45917     }
45918     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45919     // ??? needed ??? config.el = this.wrapper;
45920     
45921     
45922     
45923   
45924     // xtype created footer. - not sure if will work as we normally have to render first..
45925     if (this.footer && !this.footer.el && this.footer.xtype) {
45926         
45927         var ctr = this.grid.getView().getFooterPanel(true);
45928         this.footer.dataSource = this.grid.dataSource;
45929         this.footer = Roo.factory(this.footer, Roo);
45930         this.footer.render(ctr);
45931         
45932     }
45933     
45934     
45935     
45936     
45937      
45938 };
45939
45940 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45941 {
45942   
45943     getId : function(){
45944         return this.grid.id;
45945     },
45946     
45947     /**
45948      * Returns the grid for this panel
45949      * @return {Roo.bootstrap.Table} 
45950      */
45951     getGrid : function(){
45952         return this.grid;    
45953     },
45954     
45955     setSize : function(width, height)
45956     {
45957      
45958         //if(!this.ignoreResize(width, height)){
45959             var grid = this.grid;
45960             var size = this.adjustForComponents(width, height);
45961             // tfoot is not a footer?
45962           
45963             
45964             var gridel = grid.getGridEl();
45965             gridel.setSize(size.width, size.height);
45966             
45967             var tbd = grid.getGridEl().select('tbody', true).first();
45968             var thd = grid.getGridEl().select('thead',true).first();
45969             var tbf= grid.getGridEl().select('tfoot', true).first();
45970
45971             if (tbf) {
45972                 size.height -= tbf.getHeight();
45973             }
45974             if (thd) {
45975                 size.height -= thd.getHeight();
45976             }
45977             
45978             tbd.setSize(size.width, size.height );
45979             // this is for the account management tab -seems to work there.
45980             var thd = grid.getGridEl().select('thead',true).first();
45981             //if (tbd) {
45982             //    tbd.setSize(size.width, size.height - thd.getHeight());
45983             //}
45984              
45985             grid.autoSize();
45986         //}
45987    
45988     },
45989      
45990     
45991     
45992     beforeSlide : function(){
45993         this.grid.getView().scroller.clip();
45994     },
45995     
45996     afterSlide : function(){
45997         this.grid.getView().scroller.unclip();
45998     },
45999     
46000     destroy : function(){
46001         this.grid.destroy();
46002         delete this.grid;
46003         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46004     }
46005 });
46006
46007 /**
46008  * @class Roo.bootstrap.panel.Nest
46009  * @extends Roo.bootstrap.panel.Content
46010  * @constructor
46011  * Create a new Panel, that can contain a layout.Border.
46012  * 
46013  * 
46014  * @param {String/Object} config A string to set only the title or a config object
46015  */
46016 Roo.bootstrap.panel.Nest = function(config)
46017 {
46018     // construct with only one argument..
46019     /* FIXME - implement nicer consturctors
46020     if (layout.layout) {
46021         config = layout;
46022         layout = config.layout;
46023         delete config.layout;
46024     }
46025     if (layout.xtype && !layout.getEl) {
46026         // then layout needs constructing..
46027         layout = Roo.factory(layout, Roo);
46028     }
46029     */
46030     
46031     config.el =  config.layout.getEl();
46032     
46033     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46034     
46035     config.layout.monitorWindowResize = false; // turn off autosizing
46036     this.layout = config.layout;
46037     this.layout.getEl().addClass("roo-layout-nested-layout");
46038     this.layout.parent = this;
46039     
46040     
46041     
46042     
46043 };
46044
46045 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46046     /**
46047     * @cfg {Roo.BorderLayout} layout The layout for this panel
46048     */
46049     layout : false,
46050
46051     setSize : function(width, height){
46052         if(!this.ignoreResize(width, height)){
46053             var size = this.adjustForComponents(width, height);
46054             var el = this.layout.getEl();
46055             if (size.height < 1) {
46056                 el.setWidth(size.width);   
46057             } else {
46058                 el.setSize(size.width, size.height);
46059             }
46060             var touch = el.dom.offsetWidth;
46061             this.layout.layout();
46062             // ie requires a double layout on the first pass
46063             if(Roo.isIE && !this.initialized){
46064                 this.initialized = true;
46065                 this.layout.layout();
46066             }
46067         }
46068     },
46069     
46070     // activate all subpanels if not currently active..
46071     
46072     setActiveState : function(active){
46073         this.active = active;
46074         this.setActiveClass(active);
46075         
46076         if(!active){
46077             this.fireEvent("deactivate", this);
46078             return;
46079         }
46080         
46081         this.fireEvent("activate", this);
46082         // not sure if this should happen before or after..
46083         if (!this.layout) {
46084             return; // should not happen..
46085         }
46086         var reg = false;
46087         for (var r in this.layout.regions) {
46088             reg = this.layout.getRegion(r);
46089             if (reg.getActivePanel()) {
46090                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46091                 reg.setActivePanel(reg.getActivePanel());
46092                 continue;
46093             }
46094             if (!reg.panels.length) {
46095                 continue;
46096             }
46097             reg.showPanel(reg.getPanel(0));
46098         }
46099         
46100         
46101         
46102         
46103     },
46104     
46105     /**
46106      * Returns the nested BorderLayout for this panel
46107      * @return {Roo.BorderLayout} 
46108      */
46109     getLayout : function(){
46110         return this.layout;
46111     },
46112     
46113      /**
46114      * Adds a xtype elements to the layout of the nested panel
46115      * <pre><code>
46116
46117 panel.addxtype({
46118        xtype : 'ContentPanel',
46119        region: 'west',
46120        items: [ .... ]
46121    }
46122 );
46123
46124 panel.addxtype({
46125         xtype : 'NestedLayoutPanel',
46126         region: 'west',
46127         layout: {
46128            center: { },
46129            west: { }   
46130         },
46131         items : [ ... list of content panels or nested layout panels.. ]
46132    }
46133 );
46134 </code></pre>
46135      * @param {Object} cfg Xtype definition of item to add.
46136      */
46137     addxtype : function(cfg) {
46138         return this.layout.addxtype(cfg);
46139     
46140     }
46141 });/*
46142  * Based on:
46143  * Ext JS Library 1.1.1
46144  * Copyright(c) 2006-2007, Ext JS, LLC.
46145  *
46146  * Originally Released Under LGPL - original licence link has changed is not relivant.
46147  *
46148  * Fork - LGPL
46149  * <script type="text/javascript">
46150  */
46151 /**
46152  * @class Roo.TabPanel
46153  * @extends Roo.util.Observable
46154  * A lightweight tab container.
46155  * <br><br>
46156  * Usage:
46157  * <pre><code>
46158 // basic tabs 1, built from existing content
46159 var tabs = new Roo.TabPanel("tabs1");
46160 tabs.addTab("script", "View Script");
46161 tabs.addTab("markup", "View Markup");
46162 tabs.activate("script");
46163
46164 // more advanced tabs, built from javascript
46165 var jtabs = new Roo.TabPanel("jtabs");
46166 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46167
46168 // set up the UpdateManager
46169 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46170 var updater = tab2.getUpdateManager();
46171 updater.setDefaultUrl("ajax1.htm");
46172 tab2.on('activate', updater.refresh, updater, true);
46173
46174 // Use setUrl for Ajax loading
46175 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46176 tab3.setUrl("ajax2.htm", null, true);
46177
46178 // Disabled tab
46179 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46180 tab4.disable();
46181
46182 jtabs.activate("jtabs-1");
46183  * </code></pre>
46184  * @constructor
46185  * Create a new TabPanel.
46186  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46187  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46188  */
46189 Roo.bootstrap.panel.Tabs = function(config){
46190     /**
46191     * The container element for this TabPanel.
46192     * @type Roo.Element
46193     */
46194     this.el = Roo.get(config.el);
46195     delete config.el;
46196     if(config){
46197         if(typeof config == "boolean"){
46198             this.tabPosition = config ? "bottom" : "top";
46199         }else{
46200             Roo.apply(this, config);
46201         }
46202     }
46203     
46204     if(this.tabPosition == "bottom"){
46205         // if tabs are at the bottom = create the body first.
46206         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46207         this.el.addClass("roo-tabs-bottom");
46208     }
46209     // next create the tabs holders
46210     
46211     if (this.tabPosition == "west"){
46212         
46213         var reg = this.region; // fake it..
46214         while (reg) {
46215             if (!reg.mgr.parent) {
46216                 break;
46217             }
46218             reg = reg.mgr.parent.region;
46219         }
46220         Roo.log("got nest?");
46221         Roo.log(reg);
46222         if (reg.mgr.getRegion('west')) {
46223             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46224             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46225             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46226             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46227             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46228         
46229             
46230         }
46231         
46232         
46233     } else {
46234      
46235         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46236         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46237         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46238         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46239     }
46240     
46241     
46242     if(Roo.isIE){
46243         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46244     }
46245     
46246     // finally - if tabs are at the top, then create the body last..
46247     if(this.tabPosition != "bottom"){
46248         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46249          * @type Roo.Element
46250          */
46251         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46252         this.el.addClass("roo-tabs-top");
46253     }
46254     this.items = [];
46255
46256     this.bodyEl.setStyle("position", "relative");
46257
46258     this.active = null;
46259     this.activateDelegate = this.activate.createDelegate(this);
46260
46261     this.addEvents({
46262         /**
46263          * @event tabchange
46264          * Fires when the active tab changes
46265          * @param {Roo.TabPanel} this
46266          * @param {Roo.TabPanelItem} activePanel The new active tab
46267          */
46268         "tabchange": true,
46269         /**
46270          * @event beforetabchange
46271          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46272          * @param {Roo.TabPanel} this
46273          * @param {Object} e Set cancel to true on this object to cancel the tab change
46274          * @param {Roo.TabPanelItem} tab The tab being changed to
46275          */
46276         "beforetabchange" : true
46277     });
46278
46279     Roo.EventManager.onWindowResize(this.onResize, this);
46280     this.cpad = this.el.getPadding("lr");
46281     this.hiddenCount = 0;
46282
46283
46284     // toolbar on the tabbar support...
46285     if (this.toolbar) {
46286         alert("no toolbar support yet");
46287         this.toolbar  = false;
46288         /*
46289         var tcfg = this.toolbar;
46290         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46291         this.toolbar = new Roo.Toolbar(tcfg);
46292         if (Roo.isSafari) {
46293             var tbl = tcfg.container.child('table', true);
46294             tbl.setAttribute('width', '100%');
46295         }
46296         */
46297         
46298     }
46299    
46300
46301
46302     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46303 };
46304
46305 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46306     /*
46307      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46308      */
46309     tabPosition : "top",
46310     /*
46311      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46312      */
46313     currentTabWidth : 0,
46314     /*
46315      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46316      */
46317     minTabWidth : 40,
46318     /*
46319      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46320      */
46321     maxTabWidth : 250,
46322     /*
46323      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46324      */
46325     preferredTabWidth : 175,
46326     /*
46327      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46328      */
46329     resizeTabs : false,
46330     /*
46331      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46332      */
46333     monitorResize : true,
46334     /*
46335      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46336      */
46337     toolbar : false,  // set by caller..
46338     
46339     region : false, /// set by caller
46340     
46341     disableTooltips : true, // not used yet...
46342
46343     /**
46344      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46345      * @param {String} id The id of the div to use <b>or create</b>
46346      * @param {String} text The text for the tab
46347      * @param {String} content (optional) Content to put in the TabPanelItem body
46348      * @param {Boolean} closable (optional) True to create a close icon on the tab
46349      * @return {Roo.TabPanelItem} The created TabPanelItem
46350      */
46351     addTab : function(id, text, content, closable, tpl)
46352     {
46353         var item = new Roo.bootstrap.panel.TabItem({
46354             panel: this,
46355             id : id,
46356             text : text,
46357             closable : closable,
46358             tpl : tpl
46359         });
46360         this.addTabItem(item);
46361         if(content){
46362             item.setContent(content);
46363         }
46364         return item;
46365     },
46366
46367     /**
46368      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46369      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46370      * @return {Roo.TabPanelItem}
46371      */
46372     getTab : function(id){
46373         return this.items[id];
46374     },
46375
46376     /**
46377      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46378      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46379      */
46380     hideTab : function(id){
46381         var t = this.items[id];
46382         if(!t.isHidden()){
46383            t.setHidden(true);
46384            this.hiddenCount++;
46385            this.autoSizeTabs();
46386         }
46387     },
46388
46389     /**
46390      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46391      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46392      */
46393     unhideTab : function(id){
46394         var t = this.items[id];
46395         if(t.isHidden()){
46396            t.setHidden(false);
46397            this.hiddenCount--;
46398            this.autoSizeTabs();
46399         }
46400     },
46401
46402     /**
46403      * Adds an existing {@link Roo.TabPanelItem}.
46404      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46405      */
46406     addTabItem : function(item)
46407     {
46408         this.items[item.id] = item;
46409         this.items.push(item);
46410         this.autoSizeTabs();
46411       //  if(this.resizeTabs){
46412     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46413   //         this.autoSizeTabs();
46414 //        }else{
46415 //            item.autoSize();
46416        // }
46417     },
46418
46419     /**
46420      * Removes a {@link Roo.TabPanelItem}.
46421      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46422      */
46423     removeTab : function(id){
46424         var items = this.items;
46425         var tab = items[id];
46426         if(!tab) { return; }
46427         var index = items.indexOf(tab);
46428         if(this.active == tab && items.length > 1){
46429             var newTab = this.getNextAvailable(index);
46430             if(newTab) {
46431                 newTab.activate();
46432             }
46433         }
46434         this.stripEl.dom.removeChild(tab.pnode.dom);
46435         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46436             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46437         }
46438         items.splice(index, 1);
46439         delete this.items[tab.id];
46440         tab.fireEvent("close", tab);
46441         tab.purgeListeners();
46442         this.autoSizeTabs();
46443     },
46444
46445     getNextAvailable : function(start){
46446         var items = this.items;
46447         var index = start;
46448         // look for a next tab that will slide over to
46449         // replace the one being removed
46450         while(index < items.length){
46451             var item = items[++index];
46452             if(item && !item.isHidden()){
46453                 return item;
46454             }
46455         }
46456         // if one isn't found select the previous tab (on the left)
46457         index = start;
46458         while(index >= 0){
46459             var item = items[--index];
46460             if(item && !item.isHidden()){
46461                 return item;
46462             }
46463         }
46464         return null;
46465     },
46466
46467     /**
46468      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46469      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46470      */
46471     disableTab : function(id){
46472         var tab = this.items[id];
46473         if(tab && this.active != tab){
46474             tab.disable();
46475         }
46476     },
46477
46478     /**
46479      * Enables a {@link Roo.TabPanelItem} that is disabled.
46480      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46481      */
46482     enableTab : function(id){
46483         var tab = this.items[id];
46484         tab.enable();
46485     },
46486
46487     /**
46488      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46489      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46490      * @return {Roo.TabPanelItem} The TabPanelItem.
46491      */
46492     activate : function(id)
46493     {
46494         //Roo.log('activite:'  + id);
46495         
46496         var tab = this.items[id];
46497         if(!tab){
46498             return null;
46499         }
46500         if(tab == this.active || tab.disabled){
46501             return tab;
46502         }
46503         var e = {};
46504         this.fireEvent("beforetabchange", this, e, tab);
46505         if(e.cancel !== true && !tab.disabled){
46506             if(this.active){
46507                 this.active.hide();
46508             }
46509             this.active = this.items[id];
46510             this.active.show();
46511             this.fireEvent("tabchange", this, this.active);
46512         }
46513         return tab;
46514     },
46515
46516     /**
46517      * Gets the active {@link Roo.TabPanelItem}.
46518      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46519      */
46520     getActiveTab : function(){
46521         return this.active;
46522     },
46523
46524     /**
46525      * Updates the tab body element to fit the height of the container element
46526      * for overflow scrolling
46527      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46528      */
46529     syncHeight : function(targetHeight){
46530         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46531         var bm = this.bodyEl.getMargins();
46532         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46533         this.bodyEl.setHeight(newHeight);
46534         return newHeight;
46535     },
46536
46537     onResize : function(){
46538         if(this.monitorResize){
46539             this.autoSizeTabs();
46540         }
46541     },
46542
46543     /**
46544      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46545      */
46546     beginUpdate : function(){
46547         this.updating = true;
46548     },
46549
46550     /**
46551      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46552      */
46553     endUpdate : function(){
46554         this.updating = false;
46555         this.autoSizeTabs();
46556     },
46557
46558     /**
46559      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46560      */
46561     autoSizeTabs : function()
46562     {
46563         var count = this.items.length;
46564         var vcount = count - this.hiddenCount;
46565         
46566         if (vcount < 2) {
46567             this.stripEl.hide();
46568         } else {
46569             this.stripEl.show();
46570         }
46571         
46572         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46573             return;
46574         }
46575         
46576         
46577         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46578         var availWidth = Math.floor(w / vcount);
46579         var b = this.stripBody;
46580         if(b.getWidth() > w){
46581             var tabs = this.items;
46582             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46583             if(availWidth < this.minTabWidth){
46584                 /*if(!this.sleft){    // incomplete scrolling code
46585                     this.createScrollButtons();
46586                 }
46587                 this.showScroll();
46588                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46589             }
46590         }else{
46591             if(this.currentTabWidth < this.preferredTabWidth){
46592                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46593             }
46594         }
46595     },
46596
46597     /**
46598      * Returns the number of tabs in this TabPanel.
46599      * @return {Number}
46600      */
46601      getCount : function(){
46602          return this.items.length;
46603      },
46604
46605     /**
46606      * Resizes all the tabs to the passed width
46607      * @param {Number} The new width
46608      */
46609     setTabWidth : function(width){
46610         this.currentTabWidth = width;
46611         for(var i = 0, len = this.items.length; i < len; i++) {
46612                 if(!this.items[i].isHidden()) {
46613                 this.items[i].setWidth(width);
46614             }
46615         }
46616     },
46617
46618     /**
46619      * Destroys this TabPanel
46620      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46621      */
46622     destroy : function(removeEl){
46623         Roo.EventManager.removeResizeListener(this.onResize, this);
46624         for(var i = 0, len = this.items.length; i < len; i++){
46625             this.items[i].purgeListeners();
46626         }
46627         if(removeEl === true){
46628             this.el.update("");
46629             this.el.remove();
46630         }
46631     },
46632     
46633     createStrip : function(container)
46634     {
46635         var strip = document.createElement("nav");
46636         strip.className = Roo.bootstrap.version == 4 ?
46637             "navbar-light bg-light" : 
46638             "navbar navbar-default"; //"x-tabs-wrap";
46639         container.appendChild(strip);
46640         return strip;
46641     },
46642     
46643     createStripList : function(strip)
46644     {
46645         // div wrapper for retard IE
46646         // returns the "tr" element.
46647         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46648         //'<div class="x-tabs-strip-wrap">'+
46649           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46650           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46651         return strip.firstChild; //.firstChild.firstChild.firstChild;
46652     },
46653     createBody : function(container)
46654     {
46655         var body = document.createElement("div");
46656         Roo.id(body, "tab-body");
46657         //Roo.fly(body).addClass("x-tabs-body");
46658         Roo.fly(body).addClass("tab-content");
46659         container.appendChild(body);
46660         return body;
46661     },
46662     createItemBody :function(bodyEl, id){
46663         var body = Roo.getDom(id);
46664         if(!body){
46665             body = document.createElement("div");
46666             body.id = id;
46667         }
46668         //Roo.fly(body).addClass("x-tabs-item-body");
46669         Roo.fly(body).addClass("tab-pane");
46670          bodyEl.insertBefore(body, bodyEl.firstChild);
46671         return body;
46672     },
46673     /** @private */
46674     createStripElements :  function(stripEl, text, closable, tpl)
46675     {
46676         var td = document.createElement("li"); // was td..
46677         td.className = 'nav-item';
46678         
46679         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46680         
46681         
46682         stripEl.appendChild(td);
46683         /*if(closable){
46684             td.className = "x-tabs-closable";
46685             if(!this.closeTpl){
46686                 this.closeTpl = new Roo.Template(
46687                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46688                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46689                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46690                 );
46691             }
46692             var el = this.closeTpl.overwrite(td, {"text": text});
46693             var close = el.getElementsByTagName("div")[0];
46694             var inner = el.getElementsByTagName("em")[0];
46695             return {"el": el, "close": close, "inner": inner};
46696         } else {
46697         */
46698         // not sure what this is..
46699 //            if(!this.tabTpl){
46700                 //this.tabTpl = new Roo.Template(
46701                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46702                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46703                 //);
46704 //                this.tabTpl = new Roo.Template(
46705 //                   '<a href="#">' +
46706 //                   '<span unselectable="on"' +
46707 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46708 //                            ' >{text}</span></a>'
46709 //                );
46710 //                
46711 //            }
46712
46713
46714             var template = tpl || this.tabTpl || false;
46715             
46716             if(!template){
46717                 template =  new Roo.Template(
46718                         Roo.bootstrap.version == 4 ? 
46719                             (
46720                                 '<a class="nav-link" href="#" unselectable="on"' +
46721                                      (this.disableTooltips ? '' : ' title="{text}"') +
46722                                      ' >{text}</a>'
46723                             ) : (
46724                                 '<a class="nav-link" href="#">' +
46725                                 '<span unselectable="on"' +
46726                                          (this.disableTooltips ? '' : ' title="{text}"') +
46727                                     ' >{text}</span></a>'
46728                             )
46729                 );
46730             }
46731             
46732             switch (typeof(template)) {
46733                 case 'object' :
46734                     break;
46735                 case 'string' :
46736                     template = new Roo.Template(template);
46737                     break;
46738                 default :
46739                     break;
46740             }
46741             
46742             var el = template.overwrite(td, {"text": text});
46743             
46744             var inner = el.getElementsByTagName("span")[0];
46745             
46746             return {"el": el, "inner": inner};
46747             
46748     }
46749         
46750     
46751 });
46752
46753 /**
46754  * @class Roo.TabPanelItem
46755  * @extends Roo.util.Observable
46756  * Represents an individual item (tab plus body) in a TabPanel.
46757  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46758  * @param {String} id The id of this TabPanelItem
46759  * @param {String} text The text for the tab of this TabPanelItem
46760  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46761  */
46762 Roo.bootstrap.panel.TabItem = function(config){
46763     /**
46764      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46765      * @type Roo.TabPanel
46766      */
46767     this.tabPanel = config.panel;
46768     /**
46769      * The id for this TabPanelItem
46770      * @type String
46771      */
46772     this.id = config.id;
46773     /** @private */
46774     this.disabled = false;
46775     /** @private */
46776     this.text = config.text;
46777     /** @private */
46778     this.loaded = false;
46779     this.closable = config.closable;
46780
46781     /**
46782      * The body element for this TabPanelItem.
46783      * @type Roo.Element
46784      */
46785     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46786     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46787     this.bodyEl.setStyle("display", "block");
46788     this.bodyEl.setStyle("zoom", "1");
46789     //this.hideAction();
46790
46791     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46792     /** @private */
46793     this.el = Roo.get(els.el);
46794     this.inner = Roo.get(els.inner, true);
46795      this.textEl = Roo.bootstrap.version == 4 ?
46796         this.el : Roo.get(this.el.dom.firstChild, true);
46797
46798     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46799     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46800
46801     
46802 //    this.el.on("mousedown", this.onTabMouseDown, this);
46803     this.el.on("click", this.onTabClick, this);
46804     /** @private */
46805     if(config.closable){
46806         var c = Roo.get(els.close, true);
46807         c.dom.title = this.closeText;
46808         c.addClassOnOver("close-over");
46809         c.on("click", this.closeClick, this);
46810      }
46811
46812     this.addEvents({
46813          /**
46814          * @event activate
46815          * Fires when this tab becomes the active tab.
46816          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46817          * @param {Roo.TabPanelItem} this
46818          */
46819         "activate": true,
46820         /**
46821          * @event beforeclose
46822          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46823          * @param {Roo.TabPanelItem} this
46824          * @param {Object} e Set cancel to true on this object to cancel the close.
46825          */
46826         "beforeclose": true,
46827         /**
46828          * @event close
46829          * Fires when this tab is closed.
46830          * @param {Roo.TabPanelItem} this
46831          */
46832          "close": true,
46833         /**
46834          * @event deactivate
46835          * Fires when this tab is no longer the active tab.
46836          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46837          * @param {Roo.TabPanelItem} this
46838          */
46839          "deactivate" : true
46840     });
46841     this.hidden = false;
46842
46843     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46844 };
46845
46846 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46847            {
46848     purgeListeners : function(){
46849        Roo.util.Observable.prototype.purgeListeners.call(this);
46850        this.el.removeAllListeners();
46851     },
46852     /**
46853      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46854      */
46855     show : function(){
46856         this.status_node.addClass("active");
46857         this.showAction();
46858         if(Roo.isOpera){
46859             this.tabPanel.stripWrap.repaint();
46860         }
46861         this.fireEvent("activate", this.tabPanel, this);
46862     },
46863
46864     /**
46865      * Returns true if this tab is the active tab.
46866      * @return {Boolean}
46867      */
46868     isActive : function(){
46869         return this.tabPanel.getActiveTab() == this;
46870     },
46871
46872     /**
46873      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46874      */
46875     hide : function(){
46876         this.status_node.removeClass("active");
46877         this.hideAction();
46878         this.fireEvent("deactivate", this.tabPanel, this);
46879     },
46880
46881     hideAction : function(){
46882         this.bodyEl.hide();
46883         this.bodyEl.setStyle("position", "absolute");
46884         this.bodyEl.setLeft("-20000px");
46885         this.bodyEl.setTop("-20000px");
46886     },
46887
46888     showAction : function(){
46889         this.bodyEl.setStyle("position", "relative");
46890         this.bodyEl.setTop("");
46891         this.bodyEl.setLeft("");
46892         this.bodyEl.show();
46893     },
46894
46895     /**
46896      * Set the tooltip for the tab.
46897      * @param {String} tooltip The tab's tooltip
46898      */
46899     setTooltip : function(text){
46900         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46901             this.textEl.dom.qtip = text;
46902             this.textEl.dom.removeAttribute('title');
46903         }else{
46904             this.textEl.dom.title = text;
46905         }
46906     },
46907
46908     onTabClick : function(e){
46909         e.preventDefault();
46910         this.tabPanel.activate(this.id);
46911     },
46912
46913     onTabMouseDown : function(e){
46914         e.preventDefault();
46915         this.tabPanel.activate(this.id);
46916     },
46917 /*
46918     getWidth : function(){
46919         return this.inner.getWidth();
46920     },
46921
46922     setWidth : function(width){
46923         var iwidth = width - this.linode.getPadding("lr");
46924         this.inner.setWidth(iwidth);
46925         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46926         this.linode.setWidth(width);
46927     },
46928 */
46929     /**
46930      * Show or hide the tab
46931      * @param {Boolean} hidden True to hide or false to show.
46932      */
46933     setHidden : function(hidden){
46934         this.hidden = hidden;
46935         this.linode.setStyle("display", hidden ? "none" : "");
46936     },
46937
46938     /**
46939      * Returns true if this tab is "hidden"
46940      * @return {Boolean}
46941      */
46942     isHidden : function(){
46943         return this.hidden;
46944     },
46945
46946     /**
46947      * Returns the text for this tab
46948      * @return {String}
46949      */
46950     getText : function(){
46951         return this.text;
46952     },
46953     /*
46954     autoSize : function(){
46955         //this.el.beginMeasure();
46956         this.textEl.setWidth(1);
46957         /*
46958          *  #2804 [new] Tabs in Roojs
46959          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46960          */
46961         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46962         //this.el.endMeasure();
46963     //},
46964
46965     /**
46966      * Sets the text for the tab (Note: this also sets the tooltip text)
46967      * @param {String} text The tab's text and tooltip
46968      */
46969     setText : function(text){
46970         this.text = text;
46971         this.textEl.update(text);
46972         this.setTooltip(text);
46973         //if(!this.tabPanel.resizeTabs){
46974         //    this.autoSize();
46975         //}
46976     },
46977     /**
46978      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46979      */
46980     activate : function(){
46981         this.tabPanel.activate(this.id);
46982     },
46983
46984     /**
46985      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46986      */
46987     disable : function(){
46988         if(this.tabPanel.active != this){
46989             this.disabled = true;
46990             this.status_node.addClass("disabled");
46991         }
46992     },
46993
46994     /**
46995      * Enables this TabPanelItem if it was previously disabled.
46996      */
46997     enable : function(){
46998         this.disabled = false;
46999         this.status_node.removeClass("disabled");
47000     },
47001
47002     /**
47003      * Sets the content for this TabPanelItem.
47004      * @param {String} content The content
47005      * @param {Boolean} loadScripts true to look for and load scripts
47006      */
47007     setContent : function(content, loadScripts){
47008         this.bodyEl.update(content, loadScripts);
47009     },
47010
47011     /**
47012      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47013      * @return {Roo.UpdateManager} The UpdateManager
47014      */
47015     getUpdateManager : function(){
47016         return this.bodyEl.getUpdateManager();
47017     },
47018
47019     /**
47020      * Set a URL to be used to load the content for this TabPanelItem.
47021      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47022      * @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)
47023      * @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)
47024      * @return {Roo.UpdateManager} The UpdateManager
47025      */
47026     setUrl : function(url, params, loadOnce){
47027         if(this.refreshDelegate){
47028             this.un('activate', this.refreshDelegate);
47029         }
47030         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47031         this.on("activate", this.refreshDelegate);
47032         return this.bodyEl.getUpdateManager();
47033     },
47034
47035     /** @private */
47036     _handleRefresh : function(url, params, loadOnce){
47037         if(!loadOnce || !this.loaded){
47038             var updater = this.bodyEl.getUpdateManager();
47039             updater.update(url, params, this._setLoaded.createDelegate(this));
47040         }
47041     },
47042
47043     /**
47044      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47045      *   Will fail silently if the setUrl method has not been called.
47046      *   This does not activate the panel, just updates its content.
47047      */
47048     refresh : function(){
47049         if(this.refreshDelegate){
47050            this.loaded = false;
47051            this.refreshDelegate();
47052         }
47053     },
47054
47055     /** @private */
47056     _setLoaded : function(){
47057         this.loaded = true;
47058     },
47059
47060     /** @private */
47061     closeClick : function(e){
47062         var o = {};
47063         e.stopEvent();
47064         this.fireEvent("beforeclose", this, o);
47065         if(o.cancel !== true){
47066             this.tabPanel.removeTab(this.id);
47067         }
47068     },
47069     /**
47070      * The text displayed in the tooltip for the close icon.
47071      * @type String
47072      */
47073     closeText : "Close this tab"
47074 });
47075 /**
47076 *    This script refer to:
47077 *    Title: International Telephone Input
47078 *    Author: Jack O'Connor
47079 *    Code version:  v12.1.12
47080 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47081 **/
47082
47083 Roo.bootstrap.form.PhoneInputData = function() {
47084     var d = [
47085       [
47086         "Afghanistan (‫افغانستان‬‎)",
47087         "af",
47088         "93"
47089       ],
47090       [
47091         "Albania (Shqipëri)",
47092         "al",
47093         "355"
47094       ],
47095       [
47096         "Algeria (‫الجزائر‬‎)",
47097         "dz",
47098         "213"
47099       ],
47100       [
47101         "American Samoa",
47102         "as",
47103         "1684"
47104       ],
47105       [
47106         "Andorra",
47107         "ad",
47108         "376"
47109       ],
47110       [
47111         "Angola",
47112         "ao",
47113         "244"
47114       ],
47115       [
47116         "Anguilla",
47117         "ai",
47118         "1264"
47119       ],
47120       [
47121         "Antigua and Barbuda",
47122         "ag",
47123         "1268"
47124       ],
47125       [
47126         "Argentina",
47127         "ar",
47128         "54"
47129       ],
47130       [
47131         "Armenia (Հայաստան)",
47132         "am",
47133         "374"
47134       ],
47135       [
47136         "Aruba",
47137         "aw",
47138         "297"
47139       ],
47140       [
47141         "Australia",
47142         "au",
47143         "61",
47144         0
47145       ],
47146       [
47147         "Austria (Österreich)",
47148         "at",
47149         "43"
47150       ],
47151       [
47152         "Azerbaijan (Azərbaycan)",
47153         "az",
47154         "994"
47155       ],
47156       [
47157         "Bahamas",
47158         "bs",
47159         "1242"
47160       ],
47161       [
47162         "Bahrain (‫البحرين‬‎)",
47163         "bh",
47164         "973"
47165       ],
47166       [
47167         "Bangladesh (বাংলাদেশ)",
47168         "bd",
47169         "880"
47170       ],
47171       [
47172         "Barbados",
47173         "bb",
47174         "1246"
47175       ],
47176       [
47177         "Belarus (Беларусь)",
47178         "by",
47179         "375"
47180       ],
47181       [
47182         "Belgium (België)",
47183         "be",
47184         "32"
47185       ],
47186       [
47187         "Belize",
47188         "bz",
47189         "501"
47190       ],
47191       [
47192         "Benin (Bénin)",
47193         "bj",
47194         "229"
47195       ],
47196       [
47197         "Bermuda",
47198         "bm",
47199         "1441"
47200       ],
47201       [
47202         "Bhutan (འབྲུག)",
47203         "bt",
47204         "975"
47205       ],
47206       [
47207         "Bolivia",
47208         "bo",
47209         "591"
47210       ],
47211       [
47212         "Bosnia and Herzegovina (Босна и Херцеговина)",
47213         "ba",
47214         "387"
47215       ],
47216       [
47217         "Botswana",
47218         "bw",
47219         "267"
47220       ],
47221       [
47222         "Brazil (Brasil)",
47223         "br",
47224         "55"
47225       ],
47226       [
47227         "British Indian Ocean Territory",
47228         "io",
47229         "246"
47230       ],
47231       [
47232         "British Virgin Islands",
47233         "vg",
47234         "1284"
47235       ],
47236       [
47237         "Brunei",
47238         "bn",
47239         "673"
47240       ],
47241       [
47242         "Bulgaria (България)",
47243         "bg",
47244         "359"
47245       ],
47246       [
47247         "Burkina Faso",
47248         "bf",
47249         "226"
47250       ],
47251       [
47252         "Burundi (Uburundi)",
47253         "bi",
47254         "257"
47255       ],
47256       [
47257         "Cambodia (កម្ពុជា)",
47258         "kh",
47259         "855"
47260       ],
47261       [
47262         "Cameroon (Cameroun)",
47263         "cm",
47264         "237"
47265       ],
47266       [
47267         "Canada",
47268         "ca",
47269         "1",
47270         1,
47271         ["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"]
47272       ],
47273       [
47274         "Cape Verde (Kabu Verdi)",
47275         "cv",
47276         "238"
47277       ],
47278       [
47279         "Caribbean Netherlands",
47280         "bq",
47281         "599",
47282         1
47283       ],
47284       [
47285         "Cayman Islands",
47286         "ky",
47287         "1345"
47288       ],
47289       [
47290         "Central African Republic (République centrafricaine)",
47291         "cf",
47292         "236"
47293       ],
47294       [
47295         "Chad (Tchad)",
47296         "td",
47297         "235"
47298       ],
47299       [
47300         "Chile",
47301         "cl",
47302         "56"
47303       ],
47304       [
47305         "China (中国)",
47306         "cn",
47307         "86"
47308       ],
47309       [
47310         "Christmas Island",
47311         "cx",
47312         "61",
47313         2
47314       ],
47315       [
47316         "Cocos (Keeling) Islands",
47317         "cc",
47318         "61",
47319         1
47320       ],
47321       [
47322         "Colombia",
47323         "co",
47324         "57"
47325       ],
47326       [
47327         "Comoros (‫جزر القمر‬‎)",
47328         "km",
47329         "269"
47330       ],
47331       [
47332         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47333         "cd",
47334         "243"
47335       ],
47336       [
47337         "Congo (Republic) (Congo-Brazzaville)",
47338         "cg",
47339         "242"
47340       ],
47341       [
47342         "Cook Islands",
47343         "ck",
47344         "682"
47345       ],
47346       [
47347         "Costa Rica",
47348         "cr",
47349         "506"
47350       ],
47351       [
47352         "Côte d’Ivoire",
47353         "ci",
47354         "225"
47355       ],
47356       [
47357         "Croatia (Hrvatska)",
47358         "hr",
47359         "385"
47360       ],
47361       [
47362         "Cuba",
47363         "cu",
47364         "53"
47365       ],
47366       [
47367         "Curaçao",
47368         "cw",
47369         "599",
47370         0
47371       ],
47372       [
47373         "Cyprus (Κύπρος)",
47374         "cy",
47375         "357"
47376       ],
47377       [
47378         "Czech Republic (Česká republika)",
47379         "cz",
47380         "420"
47381       ],
47382       [
47383         "Denmark (Danmark)",
47384         "dk",
47385         "45"
47386       ],
47387       [
47388         "Djibouti",
47389         "dj",
47390         "253"
47391       ],
47392       [
47393         "Dominica",
47394         "dm",
47395         "1767"
47396       ],
47397       [
47398         "Dominican Republic (República Dominicana)",
47399         "do",
47400         "1",
47401         2,
47402         ["809", "829", "849"]
47403       ],
47404       [
47405         "Ecuador",
47406         "ec",
47407         "593"
47408       ],
47409       [
47410         "Egypt (‫مصر‬‎)",
47411         "eg",
47412         "20"
47413       ],
47414       [
47415         "El Salvador",
47416         "sv",
47417         "503"
47418       ],
47419       [
47420         "Equatorial Guinea (Guinea Ecuatorial)",
47421         "gq",
47422         "240"
47423       ],
47424       [
47425         "Eritrea",
47426         "er",
47427         "291"
47428       ],
47429       [
47430         "Estonia (Eesti)",
47431         "ee",
47432         "372"
47433       ],
47434       [
47435         "Ethiopia",
47436         "et",
47437         "251"
47438       ],
47439       [
47440         "Falkland Islands (Islas Malvinas)",
47441         "fk",
47442         "500"
47443       ],
47444       [
47445         "Faroe Islands (Føroyar)",
47446         "fo",
47447         "298"
47448       ],
47449       [
47450         "Fiji",
47451         "fj",
47452         "679"
47453       ],
47454       [
47455         "Finland (Suomi)",
47456         "fi",
47457         "358",
47458         0
47459       ],
47460       [
47461         "France",
47462         "fr",
47463         "33"
47464       ],
47465       [
47466         "French Guiana (Guyane française)",
47467         "gf",
47468         "594"
47469       ],
47470       [
47471         "French Polynesia (Polynésie française)",
47472         "pf",
47473         "689"
47474       ],
47475       [
47476         "Gabon",
47477         "ga",
47478         "241"
47479       ],
47480       [
47481         "Gambia",
47482         "gm",
47483         "220"
47484       ],
47485       [
47486         "Georgia (საქართველო)",
47487         "ge",
47488         "995"
47489       ],
47490       [
47491         "Germany (Deutschland)",
47492         "de",
47493         "49"
47494       ],
47495       [
47496         "Ghana (Gaana)",
47497         "gh",
47498         "233"
47499       ],
47500       [
47501         "Gibraltar",
47502         "gi",
47503         "350"
47504       ],
47505       [
47506         "Greece (Ελλάδα)",
47507         "gr",
47508         "30"
47509       ],
47510       [
47511         "Greenland (Kalaallit Nunaat)",
47512         "gl",
47513         "299"
47514       ],
47515       [
47516         "Grenada",
47517         "gd",
47518         "1473"
47519       ],
47520       [
47521         "Guadeloupe",
47522         "gp",
47523         "590",
47524         0
47525       ],
47526       [
47527         "Guam",
47528         "gu",
47529         "1671"
47530       ],
47531       [
47532         "Guatemala",
47533         "gt",
47534         "502"
47535       ],
47536       [
47537         "Guernsey",
47538         "gg",
47539         "44",
47540         1
47541       ],
47542       [
47543         "Guinea (Guinée)",
47544         "gn",
47545         "224"
47546       ],
47547       [
47548         "Guinea-Bissau (Guiné Bissau)",
47549         "gw",
47550         "245"
47551       ],
47552       [
47553         "Guyana",
47554         "gy",
47555         "592"
47556       ],
47557       [
47558         "Haiti",
47559         "ht",
47560         "509"
47561       ],
47562       [
47563         "Honduras",
47564         "hn",
47565         "504"
47566       ],
47567       [
47568         "Hong Kong (香港)",
47569         "hk",
47570         "852"
47571       ],
47572       [
47573         "Hungary (Magyarország)",
47574         "hu",
47575         "36"
47576       ],
47577       [
47578         "Iceland (Ísland)",
47579         "is",
47580         "354"
47581       ],
47582       [
47583         "India (भारत)",
47584         "in",
47585         "91"
47586       ],
47587       [
47588         "Indonesia",
47589         "id",
47590         "62"
47591       ],
47592       [
47593         "Iran (‫ایران‬‎)",
47594         "ir",
47595         "98"
47596       ],
47597       [
47598         "Iraq (‫العراق‬‎)",
47599         "iq",
47600         "964"
47601       ],
47602       [
47603         "Ireland",
47604         "ie",
47605         "353"
47606       ],
47607       [
47608         "Isle of Man",
47609         "im",
47610         "44",
47611         2
47612       ],
47613       [
47614         "Israel (‫ישראל‬‎)",
47615         "il",
47616         "972"
47617       ],
47618       [
47619         "Italy (Italia)",
47620         "it",
47621         "39",
47622         0
47623       ],
47624       [
47625         "Jamaica",
47626         "jm",
47627         "1876"
47628       ],
47629       [
47630         "Japan (日本)",
47631         "jp",
47632         "81"
47633       ],
47634       [
47635         "Jersey",
47636         "je",
47637         "44",
47638         3
47639       ],
47640       [
47641         "Jordan (‫الأردن‬‎)",
47642         "jo",
47643         "962"
47644       ],
47645       [
47646         "Kazakhstan (Казахстан)",
47647         "kz",
47648         "7",
47649         1
47650       ],
47651       [
47652         "Kenya",
47653         "ke",
47654         "254"
47655       ],
47656       [
47657         "Kiribati",
47658         "ki",
47659         "686"
47660       ],
47661       [
47662         "Kosovo",
47663         "xk",
47664         "383"
47665       ],
47666       [
47667         "Kuwait (‫الكويت‬‎)",
47668         "kw",
47669         "965"
47670       ],
47671       [
47672         "Kyrgyzstan (Кыргызстан)",
47673         "kg",
47674         "996"
47675       ],
47676       [
47677         "Laos (ລາວ)",
47678         "la",
47679         "856"
47680       ],
47681       [
47682         "Latvia (Latvija)",
47683         "lv",
47684         "371"
47685       ],
47686       [
47687         "Lebanon (‫لبنان‬‎)",
47688         "lb",
47689         "961"
47690       ],
47691       [
47692         "Lesotho",
47693         "ls",
47694         "266"
47695       ],
47696       [
47697         "Liberia",
47698         "lr",
47699         "231"
47700       ],
47701       [
47702         "Libya (‫ليبيا‬‎)",
47703         "ly",
47704         "218"
47705       ],
47706       [
47707         "Liechtenstein",
47708         "li",
47709         "423"
47710       ],
47711       [
47712         "Lithuania (Lietuva)",
47713         "lt",
47714         "370"
47715       ],
47716       [
47717         "Luxembourg",
47718         "lu",
47719         "352"
47720       ],
47721       [
47722         "Macau (澳門)",
47723         "mo",
47724         "853"
47725       ],
47726       [
47727         "Macedonia (FYROM) (Македонија)",
47728         "mk",
47729         "389"
47730       ],
47731       [
47732         "Madagascar (Madagasikara)",
47733         "mg",
47734         "261"
47735       ],
47736       [
47737         "Malawi",
47738         "mw",
47739         "265"
47740       ],
47741       [
47742         "Malaysia",
47743         "my",
47744         "60"
47745       ],
47746       [
47747         "Maldives",
47748         "mv",
47749         "960"
47750       ],
47751       [
47752         "Mali",
47753         "ml",
47754         "223"
47755       ],
47756       [
47757         "Malta",
47758         "mt",
47759         "356"
47760       ],
47761       [
47762         "Marshall Islands",
47763         "mh",
47764         "692"
47765       ],
47766       [
47767         "Martinique",
47768         "mq",
47769         "596"
47770       ],
47771       [
47772         "Mauritania (‫موريتانيا‬‎)",
47773         "mr",
47774         "222"
47775       ],
47776       [
47777         "Mauritius (Moris)",
47778         "mu",
47779         "230"
47780       ],
47781       [
47782         "Mayotte",
47783         "yt",
47784         "262",
47785         1
47786       ],
47787       [
47788         "Mexico (México)",
47789         "mx",
47790         "52"
47791       ],
47792       [
47793         "Micronesia",
47794         "fm",
47795         "691"
47796       ],
47797       [
47798         "Moldova (Republica Moldova)",
47799         "md",
47800         "373"
47801       ],
47802       [
47803         "Monaco",
47804         "mc",
47805         "377"
47806       ],
47807       [
47808         "Mongolia (Монгол)",
47809         "mn",
47810         "976"
47811       ],
47812       [
47813         "Montenegro (Crna Gora)",
47814         "me",
47815         "382"
47816       ],
47817       [
47818         "Montserrat",
47819         "ms",
47820         "1664"
47821       ],
47822       [
47823         "Morocco (‫المغرب‬‎)",
47824         "ma",
47825         "212",
47826         0
47827       ],
47828       [
47829         "Mozambique (Moçambique)",
47830         "mz",
47831         "258"
47832       ],
47833       [
47834         "Myanmar (Burma) (မြန်မာ)",
47835         "mm",
47836         "95"
47837       ],
47838       [
47839         "Namibia (Namibië)",
47840         "na",
47841         "264"
47842       ],
47843       [
47844         "Nauru",
47845         "nr",
47846         "674"
47847       ],
47848       [
47849         "Nepal (नेपाल)",
47850         "np",
47851         "977"
47852       ],
47853       [
47854         "Netherlands (Nederland)",
47855         "nl",
47856         "31"
47857       ],
47858       [
47859         "New Caledonia (Nouvelle-Calédonie)",
47860         "nc",
47861         "687"
47862       ],
47863       [
47864         "New Zealand",
47865         "nz",
47866         "64"
47867       ],
47868       [
47869         "Nicaragua",
47870         "ni",
47871         "505"
47872       ],
47873       [
47874         "Niger (Nijar)",
47875         "ne",
47876         "227"
47877       ],
47878       [
47879         "Nigeria",
47880         "ng",
47881         "234"
47882       ],
47883       [
47884         "Niue",
47885         "nu",
47886         "683"
47887       ],
47888       [
47889         "Norfolk Island",
47890         "nf",
47891         "672"
47892       ],
47893       [
47894         "North Korea (조선 민주주의 인민 공화국)",
47895         "kp",
47896         "850"
47897       ],
47898       [
47899         "Northern Mariana Islands",
47900         "mp",
47901         "1670"
47902       ],
47903       [
47904         "Norway (Norge)",
47905         "no",
47906         "47",
47907         0
47908       ],
47909       [
47910         "Oman (‫عُمان‬‎)",
47911         "om",
47912         "968"
47913       ],
47914       [
47915         "Pakistan (‫پاکستان‬‎)",
47916         "pk",
47917         "92"
47918       ],
47919       [
47920         "Palau",
47921         "pw",
47922         "680"
47923       ],
47924       [
47925         "Palestine (‫فلسطين‬‎)",
47926         "ps",
47927         "970"
47928       ],
47929       [
47930         "Panama (Panamá)",
47931         "pa",
47932         "507"
47933       ],
47934       [
47935         "Papua New Guinea",
47936         "pg",
47937         "675"
47938       ],
47939       [
47940         "Paraguay",
47941         "py",
47942         "595"
47943       ],
47944       [
47945         "Peru (Perú)",
47946         "pe",
47947         "51"
47948       ],
47949       [
47950         "Philippines",
47951         "ph",
47952         "63"
47953       ],
47954       [
47955         "Poland (Polska)",
47956         "pl",
47957         "48"
47958       ],
47959       [
47960         "Portugal",
47961         "pt",
47962         "351"
47963       ],
47964       [
47965         "Puerto Rico",
47966         "pr",
47967         "1",
47968         3,
47969         ["787", "939"]
47970       ],
47971       [
47972         "Qatar (‫قطر‬‎)",
47973         "qa",
47974         "974"
47975       ],
47976       [
47977         "Réunion (La Réunion)",
47978         "re",
47979         "262",
47980         0
47981       ],
47982       [
47983         "Romania (România)",
47984         "ro",
47985         "40"
47986       ],
47987       [
47988         "Russia (Россия)",
47989         "ru",
47990         "7",
47991         0
47992       ],
47993       [
47994         "Rwanda",
47995         "rw",
47996         "250"
47997       ],
47998       [
47999         "Saint Barthélemy",
48000         "bl",
48001         "590",
48002         1
48003       ],
48004       [
48005         "Saint Helena",
48006         "sh",
48007         "290"
48008       ],
48009       [
48010         "Saint Kitts and Nevis",
48011         "kn",
48012         "1869"
48013       ],
48014       [
48015         "Saint Lucia",
48016         "lc",
48017         "1758"
48018       ],
48019       [
48020         "Saint Martin (Saint-Martin (partie française))",
48021         "mf",
48022         "590",
48023         2
48024       ],
48025       [
48026         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48027         "pm",
48028         "508"
48029       ],
48030       [
48031         "Saint Vincent and the Grenadines",
48032         "vc",
48033         "1784"
48034       ],
48035       [
48036         "Samoa",
48037         "ws",
48038         "685"
48039       ],
48040       [
48041         "San Marino",
48042         "sm",
48043         "378"
48044       ],
48045       [
48046         "São Tomé and Príncipe (São Tomé e Príncipe)",
48047         "st",
48048         "239"
48049       ],
48050       [
48051         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48052         "sa",
48053         "966"
48054       ],
48055       [
48056         "Senegal (Sénégal)",
48057         "sn",
48058         "221"
48059       ],
48060       [
48061         "Serbia (Србија)",
48062         "rs",
48063         "381"
48064       ],
48065       [
48066         "Seychelles",
48067         "sc",
48068         "248"
48069       ],
48070       [
48071         "Sierra Leone",
48072         "sl",
48073         "232"
48074       ],
48075       [
48076         "Singapore",
48077         "sg",
48078         "65"
48079       ],
48080       [
48081         "Sint Maarten",
48082         "sx",
48083         "1721"
48084       ],
48085       [
48086         "Slovakia (Slovensko)",
48087         "sk",
48088         "421"
48089       ],
48090       [
48091         "Slovenia (Slovenija)",
48092         "si",
48093         "386"
48094       ],
48095       [
48096         "Solomon Islands",
48097         "sb",
48098         "677"
48099       ],
48100       [
48101         "Somalia (Soomaaliya)",
48102         "so",
48103         "252"
48104       ],
48105       [
48106         "South Africa",
48107         "za",
48108         "27"
48109       ],
48110       [
48111         "South Korea (대한민국)",
48112         "kr",
48113         "82"
48114       ],
48115       [
48116         "South Sudan (‫جنوب السودان‬‎)",
48117         "ss",
48118         "211"
48119       ],
48120       [
48121         "Spain (España)",
48122         "es",
48123         "34"
48124       ],
48125       [
48126         "Sri Lanka (ශ්‍රී ලංකාව)",
48127         "lk",
48128         "94"
48129       ],
48130       [
48131         "Sudan (‫السودان‬‎)",
48132         "sd",
48133         "249"
48134       ],
48135       [
48136         "Suriname",
48137         "sr",
48138         "597"
48139       ],
48140       [
48141         "Svalbard and Jan Mayen",
48142         "sj",
48143         "47",
48144         1
48145       ],
48146       [
48147         "Swaziland",
48148         "sz",
48149         "268"
48150       ],
48151       [
48152         "Sweden (Sverige)",
48153         "se",
48154         "46"
48155       ],
48156       [
48157         "Switzerland (Schweiz)",
48158         "ch",
48159         "41"
48160       ],
48161       [
48162         "Syria (‫سوريا‬‎)",
48163         "sy",
48164         "963"
48165       ],
48166       [
48167         "Taiwan (台灣)",
48168         "tw",
48169         "886"
48170       ],
48171       [
48172         "Tajikistan",
48173         "tj",
48174         "992"
48175       ],
48176       [
48177         "Tanzania",
48178         "tz",
48179         "255"
48180       ],
48181       [
48182         "Thailand (ไทย)",
48183         "th",
48184         "66"
48185       ],
48186       [
48187         "Timor-Leste",
48188         "tl",
48189         "670"
48190       ],
48191       [
48192         "Togo",
48193         "tg",
48194         "228"
48195       ],
48196       [
48197         "Tokelau",
48198         "tk",
48199         "690"
48200       ],
48201       [
48202         "Tonga",
48203         "to",
48204         "676"
48205       ],
48206       [
48207         "Trinidad and Tobago",
48208         "tt",
48209         "1868"
48210       ],
48211       [
48212         "Tunisia (‫تونس‬‎)",
48213         "tn",
48214         "216"
48215       ],
48216       [
48217         "Turkey (Türkiye)",
48218         "tr",
48219         "90"
48220       ],
48221       [
48222         "Turkmenistan",
48223         "tm",
48224         "993"
48225       ],
48226       [
48227         "Turks and Caicos Islands",
48228         "tc",
48229         "1649"
48230       ],
48231       [
48232         "Tuvalu",
48233         "tv",
48234         "688"
48235       ],
48236       [
48237         "U.S. Virgin Islands",
48238         "vi",
48239         "1340"
48240       ],
48241       [
48242         "Uganda",
48243         "ug",
48244         "256"
48245       ],
48246       [
48247         "Ukraine (Україна)",
48248         "ua",
48249         "380"
48250       ],
48251       [
48252         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48253         "ae",
48254         "971"
48255       ],
48256       [
48257         "United Kingdom",
48258         "gb",
48259         "44",
48260         0
48261       ],
48262       [
48263         "United States",
48264         "us",
48265         "1",
48266         0
48267       ],
48268       [
48269         "Uruguay",
48270         "uy",
48271         "598"
48272       ],
48273       [
48274         "Uzbekistan (Oʻzbekiston)",
48275         "uz",
48276         "998"
48277       ],
48278       [
48279         "Vanuatu",
48280         "vu",
48281         "678"
48282       ],
48283       [
48284         "Vatican City (Città del Vaticano)",
48285         "va",
48286         "39",
48287         1
48288       ],
48289       [
48290         "Venezuela",
48291         "ve",
48292         "58"
48293       ],
48294       [
48295         "Vietnam (Việt Nam)",
48296         "vn",
48297         "84"
48298       ],
48299       [
48300         "Wallis and Futuna (Wallis-et-Futuna)",
48301         "wf",
48302         "681"
48303       ],
48304       [
48305         "Western Sahara (‫الصحراء الغربية‬‎)",
48306         "eh",
48307         "212",
48308         1
48309       ],
48310       [
48311         "Yemen (‫اليمن‬‎)",
48312         "ye",
48313         "967"
48314       ],
48315       [
48316         "Zambia",
48317         "zm",
48318         "260"
48319       ],
48320       [
48321         "Zimbabwe",
48322         "zw",
48323         "263"
48324       ],
48325       [
48326         "Åland Islands",
48327         "ax",
48328         "358",
48329         1
48330       ]
48331   ];
48332   
48333   return d;
48334 }/**
48335 *    This script refer to:
48336 *    Title: International Telephone Input
48337 *    Author: Jack O'Connor
48338 *    Code version:  v12.1.12
48339 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48340 **/
48341
48342 /**
48343  * @class Roo.bootstrap.form.PhoneInput
48344  * @extends Roo.bootstrap.form.TriggerField
48345  * An input with International dial-code selection
48346  
48347  * @cfg {String} defaultDialCode default '+852'
48348  * @cfg {Array} preferedCountries default []
48349   
48350  * @constructor
48351  * Create a new PhoneInput.
48352  * @param {Object} config Configuration options
48353  */
48354
48355 Roo.bootstrap.form.PhoneInput = function(config) {
48356     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48357 };
48358
48359 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48360         /**
48361         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48362         */
48363         listWidth: undefined,
48364         
48365         selectedClass: 'active',
48366         
48367         invalidClass : "has-warning",
48368         
48369         validClass: 'has-success',
48370         
48371         allowed: '0123456789',
48372         
48373         max_length: 15,
48374         
48375         /**
48376          * @cfg {String} defaultDialCode The default dial code when initializing the input
48377          */
48378         defaultDialCode: '+852',
48379         
48380         /**
48381          * @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
48382          */
48383         preferedCountries: false,
48384         
48385         getAutoCreate : function()
48386         {
48387             var data = Roo.bootstrap.form.PhoneInputData();
48388             var align = this.labelAlign || this.parentLabelAlign();
48389             var id = Roo.id();
48390             
48391             this.allCountries = [];
48392             this.dialCodeMapping = [];
48393             
48394             for (var i = 0; i < data.length; i++) {
48395               var c = data[i];
48396               this.allCountries[i] = {
48397                 name: c[0],
48398                 iso2: c[1],
48399                 dialCode: c[2],
48400                 priority: c[3] || 0,
48401                 areaCodes: c[4] || null
48402               };
48403               this.dialCodeMapping[c[2]] = {
48404                   name: c[0],
48405                   iso2: c[1],
48406                   priority: c[3] || 0,
48407                   areaCodes: c[4] || null
48408               };
48409             }
48410             
48411             var cfg = {
48412                 cls: 'form-group',
48413                 cn: []
48414             };
48415             
48416             var input =  {
48417                 tag: 'input',
48418                 id : id,
48419                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48420                 maxlength: this.max_length,
48421                 cls : 'form-control tel-input',
48422                 autocomplete: 'new-password'
48423             };
48424             
48425             var hiddenInput = {
48426                 tag: 'input',
48427                 type: 'hidden',
48428                 cls: 'hidden-tel-input'
48429             };
48430             
48431             if (this.name) {
48432                 hiddenInput.name = this.name;
48433             }
48434             
48435             if (this.disabled) {
48436                 input.disabled = true;
48437             }
48438             
48439             var flag_container = {
48440                 tag: 'div',
48441                 cls: 'flag-box',
48442                 cn: [
48443                     {
48444                         tag: 'div',
48445                         cls: 'flag'
48446                     },
48447                     {
48448                         tag: 'div',
48449                         cls: 'caret'
48450                     }
48451                 ]
48452             };
48453             
48454             var box = {
48455                 tag: 'div',
48456                 cls: this.hasFeedback ? 'has-feedback' : '',
48457                 cn: [
48458                     hiddenInput,
48459                     input,
48460                     {
48461                         tag: 'input',
48462                         cls: 'dial-code-holder',
48463                         disabled: true
48464                     }
48465                 ]
48466             };
48467             
48468             var container = {
48469                 cls: 'roo-select2-container input-group',
48470                 cn: [
48471                     flag_container,
48472                     box
48473                 ]
48474             };
48475             
48476             if (this.fieldLabel.length) {
48477                 var indicator = {
48478                     tag: 'i',
48479                     tooltip: 'This field is required'
48480                 };
48481                 
48482                 var label = {
48483                     tag: 'label',
48484                     'for':  id,
48485                     cls: 'control-label',
48486                     cn: []
48487                 };
48488                 
48489                 var label_text = {
48490                     tag: 'span',
48491                     html: this.fieldLabel
48492                 };
48493                 
48494                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48495                 label.cn = [
48496                     indicator,
48497                     label_text
48498                 ];
48499                 
48500                 if(this.indicatorpos == 'right') {
48501                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48502                     label.cn = [
48503                         label_text,
48504                         indicator
48505                     ];
48506                 }
48507                 
48508                 if(align == 'left') {
48509                     container = {
48510                         tag: 'div',
48511                         cn: [
48512                             container
48513                         ]
48514                     };
48515                     
48516                     if(this.labelWidth > 12){
48517                         label.style = "width: " + this.labelWidth + 'px';
48518                     }
48519                     if(this.labelWidth < 13 && this.labelmd == 0){
48520                         this.labelmd = this.labelWidth;
48521                     }
48522                     if(this.labellg > 0){
48523                         label.cls += ' col-lg-' + this.labellg;
48524                         input.cls += ' col-lg-' + (12 - this.labellg);
48525                     }
48526                     if(this.labelmd > 0){
48527                         label.cls += ' col-md-' + this.labelmd;
48528                         container.cls += ' col-md-' + (12 - this.labelmd);
48529                     }
48530                     if(this.labelsm > 0){
48531                         label.cls += ' col-sm-' + this.labelsm;
48532                         container.cls += ' col-sm-' + (12 - this.labelsm);
48533                     }
48534                     if(this.labelxs > 0){
48535                         label.cls += ' col-xs-' + this.labelxs;
48536                         container.cls += ' col-xs-' + (12 - this.labelxs);
48537                     }
48538                 }
48539             }
48540             
48541             cfg.cn = [
48542                 label,
48543                 container
48544             ];
48545             
48546             var settings = this;
48547             
48548             ['xs','sm','md','lg'].map(function(size){
48549                 if (settings[size]) {
48550                     cfg.cls += ' col-' + size + '-' + settings[size];
48551                 }
48552             });
48553             
48554             this.store = new Roo.data.Store({
48555                 proxy : new Roo.data.MemoryProxy({}),
48556                 reader : new Roo.data.JsonReader({
48557                     fields : [
48558                         {
48559                             'name' : 'name',
48560                             'type' : 'string'
48561                         },
48562                         {
48563                             'name' : 'iso2',
48564                             'type' : 'string'
48565                         },
48566                         {
48567                             'name' : 'dialCode',
48568                             'type' : 'string'
48569                         },
48570                         {
48571                             'name' : 'priority',
48572                             'type' : 'string'
48573                         },
48574                         {
48575                             'name' : 'areaCodes',
48576                             'type' : 'string'
48577                         }
48578                     ]
48579                 })
48580             });
48581             
48582             if(!this.preferedCountries) {
48583                 this.preferedCountries = [
48584                     'hk',
48585                     'gb',
48586                     'us'
48587                 ];
48588             }
48589             
48590             var p = this.preferedCountries.reverse();
48591             
48592             if(p) {
48593                 for (var i = 0; i < p.length; i++) {
48594                     for (var j = 0; j < this.allCountries.length; j++) {
48595                         if(this.allCountries[j].iso2 == p[i]) {
48596                             var t = this.allCountries[j];
48597                             this.allCountries.splice(j,1);
48598                             this.allCountries.unshift(t);
48599                         }
48600                     } 
48601                 }
48602             }
48603             
48604             this.store.proxy.data = {
48605                 success: true,
48606                 data: this.allCountries
48607             };
48608             
48609             return cfg;
48610         },
48611         
48612         initEvents : function()
48613         {
48614             this.createList();
48615             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48616             
48617             this.indicator = this.indicatorEl();
48618             this.flag = this.flagEl();
48619             this.dialCodeHolder = this.dialCodeHolderEl();
48620             
48621             this.trigger = this.el.select('div.flag-box',true).first();
48622             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48623             
48624             var _this = this;
48625             
48626             (function(){
48627                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48628                 _this.list.setWidth(lw);
48629             }).defer(100);
48630             
48631             this.list.on('mouseover', this.onViewOver, this);
48632             this.list.on('mousemove', this.onViewMove, this);
48633             this.inputEl().on("keyup", this.onKeyUp, this);
48634             this.inputEl().on("keypress", this.onKeyPress, this);
48635             
48636             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48637
48638             this.view = new Roo.View(this.list, this.tpl, {
48639                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48640             });
48641             
48642             this.view.on('click', this.onViewClick, this);
48643             this.setValue(this.defaultDialCode);
48644         },
48645         
48646         onTriggerClick : function(e)
48647         {
48648             Roo.log('trigger click');
48649             if(this.disabled){
48650                 return;
48651             }
48652             
48653             if(this.isExpanded()){
48654                 this.collapse();
48655                 this.hasFocus = false;
48656             }else {
48657                 this.store.load({});
48658                 this.hasFocus = true;
48659                 this.expand();
48660             }
48661         },
48662         
48663         isExpanded : function()
48664         {
48665             return this.list.isVisible();
48666         },
48667         
48668         collapse : function()
48669         {
48670             if(!this.isExpanded()){
48671                 return;
48672             }
48673             this.list.hide();
48674             Roo.get(document).un('mousedown', this.collapseIf, this);
48675             Roo.get(document).un('mousewheel', this.collapseIf, this);
48676             this.fireEvent('collapse', this);
48677             this.validate();
48678         },
48679         
48680         expand : function()
48681         {
48682             Roo.log('expand');
48683
48684             if(this.isExpanded() || !this.hasFocus){
48685                 return;
48686             }
48687             
48688             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48689             this.list.setWidth(lw);
48690             
48691             this.list.show();
48692             this.restrictHeight();
48693             
48694             Roo.get(document).on('mousedown', this.collapseIf, this);
48695             Roo.get(document).on('mousewheel', this.collapseIf, this);
48696             
48697             this.fireEvent('expand', this);
48698         },
48699         
48700         restrictHeight : function()
48701         {
48702             this.list.alignTo(this.inputEl(), this.listAlign);
48703             this.list.alignTo(this.inputEl(), this.listAlign);
48704         },
48705         
48706         onViewOver : function(e, t)
48707         {
48708             if(this.inKeyMode){
48709                 return;
48710             }
48711             var item = this.view.findItemFromChild(t);
48712             
48713             if(item){
48714                 var index = this.view.indexOf(item);
48715                 this.select(index, false);
48716             }
48717         },
48718
48719         // private
48720         onViewClick : function(view, doFocus, el, e)
48721         {
48722             var index = this.view.getSelectedIndexes()[0];
48723             
48724             var r = this.store.getAt(index);
48725             
48726             if(r){
48727                 this.onSelect(r, index);
48728             }
48729             if(doFocus !== false && !this.blockFocus){
48730                 this.inputEl().focus();
48731             }
48732         },
48733         
48734         onViewMove : function(e, t)
48735         {
48736             this.inKeyMode = false;
48737         },
48738         
48739         select : function(index, scrollIntoView)
48740         {
48741             this.selectedIndex = index;
48742             this.view.select(index);
48743             if(scrollIntoView !== false){
48744                 var el = this.view.getNode(index);
48745                 if(el){
48746                     this.list.scrollChildIntoView(el, false);
48747                 }
48748             }
48749         },
48750         
48751         createList : function()
48752         {
48753             this.list = Roo.get(document.body).createChild({
48754                 tag: 'ul',
48755                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48756                 style: 'display:none'
48757             });
48758             
48759             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48760         },
48761         
48762         collapseIf : function(e)
48763         {
48764             var in_combo  = e.within(this.el);
48765             var in_list =  e.within(this.list);
48766             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48767             
48768             if (in_combo || in_list || is_list) {
48769                 return;
48770             }
48771             this.collapse();
48772         },
48773         
48774         onSelect : function(record, index)
48775         {
48776             if(this.fireEvent('beforeselect', this, record, index) !== false){
48777                 
48778                 this.setFlagClass(record.data.iso2);
48779                 this.setDialCode(record.data.dialCode);
48780                 this.hasFocus = false;
48781                 this.collapse();
48782                 this.fireEvent('select', this, record, index);
48783             }
48784         },
48785         
48786         flagEl : function()
48787         {
48788             var flag = this.el.select('div.flag',true).first();
48789             if(!flag){
48790                 return false;
48791             }
48792             return flag;
48793         },
48794         
48795         dialCodeHolderEl : function()
48796         {
48797             var d = this.el.select('input.dial-code-holder',true).first();
48798             if(!d){
48799                 return false;
48800             }
48801             return d;
48802         },
48803         
48804         setDialCode : function(v)
48805         {
48806             this.dialCodeHolder.dom.value = '+'+v;
48807         },
48808         
48809         setFlagClass : function(n)
48810         {
48811             this.flag.dom.className = 'flag '+n;
48812         },
48813         
48814         getValue : function()
48815         {
48816             var v = this.inputEl().getValue();
48817             if(this.dialCodeHolder) {
48818                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48819             }
48820             return v;
48821         },
48822         
48823         setValue : function(v)
48824         {
48825             var d = this.getDialCode(v);
48826             
48827             //invalid dial code
48828             if(v.length == 0 || !d || d.length == 0) {
48829                 if(this.rendered){
48830                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48831                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48832                 }
48833                 return;
48834             }
48835             
48836             //valid dial code
48837             this.setFlagClass(this.dialCodeMapping[d].iso2);
48838             this.setDialCode(d);
48839             this.inputEl().dom.value = v.replace('+'+d,'');
48840             this.hiddenEl().dom.value = this.getValue();
48841             
48842             this.validate();
48843         },
48844         
48845         getDialCode : function(v)
48846         {
48847             v = v ||  '';
48848             
48849             if (v.length == 0) {
48850                 return this.dialCodeHolder.dom.value;
48851             }
48852             
48853             var dialCode = "";
48854             if (v.charAt(0) != "+") {
48855                 return false;
48856             }
48857             var numericChars = "";
48858             for (var i = 1; i < v.length; i++) {
48859               var c = v.charAt(i);
48860               if (!isNaN(c)) {
48861                 numericChars += c;
48862                 if (this.dialCodeMapping[numericChars]) {
48863                   dialCode = v.substr(1, i);
48864                 }
48865                 if (numericChars.length == 4) {
48866                   break;
48867                 }
48868               }
48869             }
48870             return dialCode;
48871         },
48872         
48873         reset : function()
48874         {
48875             this.setValue(this.defaultDialCode);
48876             this.validate();
48877         },
48878         
48879         hiddenEl : function()
48880         {
48881             return this.el.select('input.hidden-tel-input',true).first();
48882         },
48883         
48884         // after setting val
48885         onKeyUp : function(e){
48886             this.setValue(this.getValue());
48887         },
48888         
48889         onKeyPress : function(e){
48890             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48891                 e.stopEvent();
48892             }
48893         }
48894         
48895 });
48896 /**
48897  * @class Roo.bootstrap.form.MoneyField
48898  * @extends Roo.bootstrap.form.ComboBox
48899  * Bootstrap MoneyField class
48900  * 
48901  * @constructor
48902  * Create a new MoneyField.
48903  * @param {Object} config Configuration options
48904  */
48905
48906 Roo.bootstrap.form.MoneyField = function(config) {
48907     
48908     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48909     
48910 };
48911
48912 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48913     
48914     /**
48915      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48916      */
48917     allowDecimals : true,
48918     /**
48919      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48920      */
48921     decimalSeparator : ".",
48922     /**
48923      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48924      */
48925     decimalPrecision : 0,
48926     /**
48927      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48928      */
48929     allowNegative : true,
48930     /**
48931      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48932      */
48933     allowZero: true,
48934     /**
48935      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48936      */
48937     minValue : Number.NEGATIVE_INFINITY,
48938     /**
48939      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48940      */
48941     maxValue : Number.MAX_VALUE,
48942     /**
48943      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48944      */
48945     minText : "The minimum value for this field is {0}",
48946     /**
48947      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48948      */
48949     maxText : "The maximum value for this field is {0}",
48950     /**
48951      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48952      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48953      */
48954     nanText : "{0} is not a valid number",
48955     /**
48956      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48957      */
48958     castInt : true,
48959     /**
48960      * @cfg {String} defaults currency of the MoneyField
48961      * value should be in lkey
48962      */
48963     defaultCurrency : false,
48964     /**
48965      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48966      */
48967     thousandsDelimiter : false,
48968     /**
48969      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48970      */
48971     max_length: false,
48972     
48973     inputlg : 9,
48974     inputmd : 9,
48975     inputsm : 9,
48976     inputxs : 6,
48977      /**
48978      * @cfg {Roo.data.Store} store  Store to lookup currency??
48979      */
48980     store : false,
48981     
48982     getAutoCreate : function()
48983     {
48984         var align = this.labelAlign || this.parentLabelAlign();
48985         
48986         var id = Roo.id();
48987
48988         var cfg = {
48989             cls: 'form-group',
48990             cn: []
48991         };
48992
48993         var input =  {
48994             tag: 'input',
48995             id : id,
48996             cls : 'form-control roo-money-amount-input',
48997             autocomplete: 'new-password'
48998         };
48999         
49000         var hiddenInput = {
49001             tag: 'input',
49002             type: 'hidden',
49003             id: Roo.id(),
49004             cls: 'hidden-number-input'
49005         };
49006         
49007         if(this.max_length) {
49008             input.maxlength = this.max_length; 
49009         }
49010         
49011         if (this.name) {
49012             hiddenInput.name = this.name;
49013         }
49014
49015         if (this.disabled) {
49016             input.disabled = true;
49017         }
49018
49019         var clg = 12 - this.inputlg;
49020         var cmd = 12 - this.inputmd;
49021         var csm = 12 - this.inputsm;
49022         var cxs = 12 - this.inputxs;
49023         
49024         var container = {
49025             tag : 'div',
49026             cls : 'row roo-money-field',
49027             cn : [
49028                 {
49029                     tag : 'div',
49030                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49031                     cn : [
49032                         {
49033                             tag : 'div',
49034                             cls: 'roo-select2-container input-group',
49035                             cn: [
49036                                 {
49037                                     tag : 'input',
49038                                     cls : 'form-control roo-money-currency-input',
49039                                     autocomplete: 'new-password',
49040                                     readOnly : 1,
49041                                     name : this.currencyName
49042                                 },
49043                                 {
49044                                     tag :'span',
49045                                     cls : 'input-group-addon',
49046                                     cn : [
49047                                         {
49048                                             tag: 'span',
49049                                             cls: 'caret'
49050                                         }
49051                                     ]
49052                                 }
49053                             ]
49054                         }
49055                     ]
49056                 },
49057                 {
49058                     tag : 'div',
49059                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49060                     cn : [
49061                         {
49062                             tag: 'div',
49063                             cls: this.hasFeedback ? 'has-feedback' : '',
49064                             cn: [
49065                                 input
49066                             ]
49067                         }
49068                     ]
49069                 }
49070             ]
49071             
49072         };
49073         
49074         if (this.fieldLabel.length) {
49075             var indicator = {
49076                 tag: 'i',
49077                 tooltip: 'This field is required'
49078             };
49079
49080             var label = {
49081                 tag: 'label',
49082                 'for':  id,
49083                 cls: 'control-label',
49084                 cn: []
49085             };
49086
49087             var label_text = {
49088                 tag: 'span',
49089                 html: this.fieldLabel
49090             };
49091
49092             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49093             label.cn = [
49094                 indicator,
49095                 label_text
49096             ];
49097
49098             if(this.indicatorpos == 'right') {
49099                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49100                 label.cn = [
49101                     label_text,
49102                     indicator
49103                 ];
49104             }
49105
49106             if(align == 'left') {
49107                 container = {
49108                     tag: 'div',
49109                     cn: [
49110                         container
49111                     ]
49112                 };
49113
49114                 if(this.labelWidth > 12){
49115                     label.style = "width: " + this.labelWidth + 'px';
49116                 }
49117                 if(this.labelWidth < 13 && this.labelmd == 0){
49118                     this.labelmd = this.labelWidth;
49119                 }
49120                 if(this.labellg > 0){
49121                     label.cls += ' col-lg-' + this.labellg;
49122                     input.cls += ' col-lg-' + (12 - this.labellg);
49123                 }
49124                 if(this.labelmd > 0){
49125                     label.cls += ' col-md-' + this.labelmd;
49126                     container.cls += ' col-md-' + (12 - this.labelmd);
49127                 }
49128                 if(this.labelsm > 0){
49129                     label.cls += ' col-sm-' + this.labelsm;
49130                     container.cls += ' col-sm-' + (12 - this.labelsm);
49131                 }
49132                 if(this.labelxs > 0){
49133                     label.cls += ' col-xs-' + this.labelxs;
49134                     container.cls += ' col-xs-' + (12 - this.labelxs);
49135                 }
49136             }
49137         }
49138
49139         cfg.cn = [
49140             label,
49141             container,
49142             hiddenInput
49143         ];
49144         
49145         var settings = this;
49146
49147         ['xs','sm','md','lg'].map(function(size){
49148             if (settings[size]) {
49149                 cfg.cls += ' col-' + size + '-' + settings[size];
49150             }
49151         });
49152         
49153         return cfg;
49154     },
49155     
49156     initEvents : function()
49157     {
49158         this.indicator = this.indicatorEl();
49159         
49160         this.initCurrencyEvent();
49161         
49162         this.initNumberEvent();
49163     },
49164     
49165     initCurrencyEvent : function()
49166     {
49167         if (!this.store) {
49168             throw "can not find store for combo";
49169         }
49170         
49171         this.store = Roo.factory(this.store, Roo.data);
49172         this.store.parent = this;
49173         
49174         this.createList();
49175         
49176         this.triggerEl = this.el.select('.input-group-addon', true).first();
49177         
49178         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49179         
49180         var _this = this;
49181         
49182         (function(){
49183             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49184             _this.list.setWidth(lw);
49185         }).defer(100);
49186         
49187         this.list.on('mouseover', this.onViewOver, this);
49188         this.list.on('mousemove', this.onViewMove, this);
49189         this.list.on('scroll', this.onViewScroll, this);
49190         
49191         if(!this.tpl){
49192             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49193         }
49194         
49195         this.view = new Roo.View(this.list, this.tpl, {
49196             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49197         });
49198         
49199         this.view.on('click', this.onViewClick, this);
49200         
49201         this.store.on('beforeload', this.onBeforeLoad, this);
49202         this.store.on('load', this.onLoad, this);
49203         this.store.on('loadexception', this.onLoadException, this);
49204         
49205         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49206             "up" : function(e){
49207                 this.inKeyMode = true;
49208                 this.selectPrev();
49209             },
49210
49211             "down" : function(e){
49212                 if(!this.isExpanded()){
49213                     this.onTriggerClick();
49214                 }else{
49215                     this.inKeyMode = true;
49216                     this.selectNext();
49217                 }
49218             },
49219
49220             "enter" : function(e){
49221                 this.collapse();
49222                 
49223                 if(this.fireEvent("specialkey", this, e)){
49224                     this.onViewClick(false);
49225                 }
49226                 
49227                 return true;
49228             },
49229
49230             "esc" : function(e){
49231                 this.collapse();
49232             },
49233
49234             "tab" : function(e){
49235                 this.collapse();
49236                 
49237                 if(this.fireEvent("specialkey", this, e)){
49238                     this.onViewClick(false);
49239                 }
49240                 
49241                 return true;
49242             },
49243
49244             scope : this,
49245
49246             doRelay : function(foo, bar, hname){
49247                 if(hname == 'down' || this.scope.isExpanded()){
49248                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49249                 }
49250                 return true;
49251             },
49252
49253             forceKeyDown: true
49254         });
49255         
49256         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49257         
49258     },
49259     
49260     initNumberEvent : function(e)
49261     {
49262         this.inputEl().on("keydown" , this.fireKey,  this);
49263         this.inputEl().on("focus", this.onFocus,  this);
49264         this.inputEl().on("blur", this.onBlur,  this);
49265         
49266         this.inputEl().relayEvent('keyup', this);
49267         
49268         if(this.indicator){
49269             this.indicator.addClass('invisible');
49270         }
49271  
49272         this.originalValue = this.getValue();
49273         
49274         if(this.validationEvent == 'keyup'){
49275             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49276             this.inputEl().on('keyup', this.filterValidation, this);
49277         }
49278         else if(this.validationEvent !== false){
49279             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49280         }
49281         
49282         if(this.selectOnFocus){
49283             this.on("focus", this.preFocus, this);
49284             
49285         }
49286         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49287             this.inputEl().on("keypress", this.filterKeys, this);
49288         } else {
49289             this.inputEl().relayEvent('keypress', this);
49290         }
49291         
49292         var allowed = "0123456789";
49293         
49294         if(this.allowDecimals){
49295             allowed += this.decimalSeparator;
49296         }
49297         
49298         if(this.allowNegative){
49299             allowed += "-";
49300         }
49301         
49302         if(this.thousandsDelimiter) {
49303             allowed += ",";
49304         }
49305         
49306         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49307         
49308         var keyPress = function(e){
49309             
49310             var k = e.getKey();
49311             
49312             var c = e.getCharCode();
49313             
49314             if(
49315                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49316                     allowed.indexOf(String.fromCharCode(c)) === -1
49317             ){
49318                 e.stopEvent();
49319                 return;
49320             }
49321             
49322             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49323                 return;
49324             }
49325             
49326             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49327                 e.stopEvent();
49328             }
49329         };
49330         
49331         this.inputEl().on("keypress", keyPress, this);
49332         
49333     },
49334     
49335     onTriggerClick : function(e)
49336     {   
49337         if(this.disabled){
49338             return;
49339         }
49340         
49341         this.page = 0;
49342         this.loadNext = false;
49343         
49344         if(this.isExpanded()){
49345             this.collapse();
49346             return;
49347         }
49348         
49349         this.hasFocus = true;
49350         
49351         if(this.triggerAction == 'all') {
49352             this.doQuery(this.allQuery, true);
49353             return;
49354         }
49355         
49356         this.doQuery(this.getRawValue());
49357     },
49358     
49359     getCurrency : function()
49360     {   
49361         var v = this.currencyEl().getValue();
49362         
49363         return v;
49364     },
49365     
49366     restrictHeight : function()
49367     {
49368         this.list.alignTo(this.currencyEl(), this.listAlign);
49369         this.list.alignTo(this.currencyEl(), this.listAlign);
49370     },
49371     
49372     onViewClick : function(view, doFocus, el, e)
49373     {
49374         var index = this.view.getSelectedIndexes()[0];
49375         
49376         var r = this.store.getAt(index);
49377         
49378         if(r){
49379             this.onSelect(r, index);
49380         }
49381     },
49382     
49383     onSelect : function(record, index){
49384         
49385         if(this.fireEvent('beforeselect', this, record, index) !== false){
49386         
49387             this.setFromCurrencyData(index > -1 ? record.data : false);
49388             
49389             this.collapse();
49390             
49391             this.fireEvent('select', this, record, index);
49392         }
49393     },
49394     
49395     setFromCurrencyData : function(o)
49396     {
49397         var currency = '';
49398         
49399         this.lastCurrency = o;
49400         
49401         if (this.currencyField) {
49402             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49403         } else {
49404             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49405         }
49406         
49407         this.lastSelectionText = currency;
49408         
49409         //setting default currency
49410         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49411             this.setCurrency(this.defaultCurrency);
49412             return;
49413         }
49414         
49415         this.setCurrency(currency);
49416     },
49417     
49418     setFromData : function(o)
49419     {
49420         var c = {};
49421         
49422         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49423         
49424         this.setFromCurrencyData(c);
49425         
49426         var value = '';
49427         
49428         if (this.name) {
49429             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49430         } else {
49431             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49432         }
49433         
49434         this.setValue(value);
49435         
49436     },
49437     
49438     setCurrency : function(v)
49439     {   
49440         this.currencyValue = v;
49441         
49442         if(this.rendered){
49443             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49444             this.validate();
49445         }
49446     },
49447     
49448     setValue : function(v)
49449     {
49450         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49451         
49452         this.value = v;
49453         
49454         if(this.rendered){
49455             
49456             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49457             
49458             this.inputEl().dom.value = (v == '') ? '' :
49459                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49460             
49461             if(!this.allowZero && v === '0') {
49462                 this.hiddenEl().dom.value = '';
49463                 this.inputEl().dom.value = '';
49464             }
49465             
49466             this.validate();
49467         }
49468     },
49469     
49470     getRawValue : function()
49471     {
49472         var v = this.inputEl().getValue();
49473         
49474         return v;
49475     },
49476     
49477     getValue : function()
49478     {
49479         return this.fixPrecision(this.parseValue(this.getRawValue()));
49480     },
49481     
49482     parseValue : function(value)
49483     {
49484         if(this.thousandsDelimiter) {
49485             value += "";
49486             r = new RegExp(",", "g");
49487             value = value.replace(r, "");
49488         }
49489         
49490         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49491         return isNaN(value) ? '' : value;
49492         
49493     },
49494     
49495     fixPrecision : function(value)
49496     {
49497         if(this.thousandsDelimiter) {
49498             value += "";
49499             r = new RegExp(",", "g");
49500             value = value.replace(r, "");
49501         }
49502         
49503         var nan = isNaN(value);
49504         
49505         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49506             return nan ? '' : value;
49507         }
49508         return parseFloat(value).toFixed(this.decimalPrecision);
49509     },
49510     
49511     decimalPrecisionFcn : function(v)
49512     {
49513         return Math.floor(v);
49514     },
49515     
49516     validateValue : function(value)
49517     {
49518         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49519             return false;
49520         }
49521         
49522         var num = this.parseValue(value);
49523         
49524         if(isNaN(num)){
49525             this.markInvalid(String.format(this.nanText, value));
49526             return false;
49527         }
49528         
49529         if(num < this.minValue){
49530             this.markInvalid(String.format(this.minText, this.minValue));
49531             return false;
49532         }
49533         
49534         if(num > this.maxValue){
49535             this.markInvalid(String.format(this.maxText, this.maxValue));
49536             return false;
49537         }
49538         
49539         return true;
49540     },
49541     
49542     validate : function()
49543     {
49544         if(this.disabled || this.allowBlank){
49545             this.markValid();
49546             return true;
49547         }
49548         
49549         var currency = this.getCurrency();
49550         
49551         if(this.validateValue(this.getRawValue()) && currency.length){
49552             this.markValid();
49553             return true;
49554         }
49555         
49556         this.markInvalid();
49557         return false;
49558     },
49559     
49560     getName: function()
49561     {
49562         return this.name;
49563     },
49564     
49565     beforeBlur : function()
49566     {
49567         if(!this.castInt){
49568             return;
49569         }
49570         
49571         var v = this.parseValue(this.getRawValue());
49572         
49573         if(v || v == 0){
49574             this.setValue(v);
49575         }
49576     },
49577     
49578     onBlur : function()
49579     {
49580         this.beforeBlur();
49581         
49582         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49583             //this.el.removeClass(this.focusClass);
49584         }
49585         
49586         this.hasFocus = false;
49587         
49588         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49589             this.validate();
49590         }
49591         
49592         var v = this.getValue();
49593         
49594         if(String(v) !== String(this.startValue)){
49595             this.fireEvent('change', this, v, this.startValue);
49596         }
49597         
49598         this.fireEvent("blur", this);
49599     },
49600     
49601     inputEl : function()
49602     {
49603         return this.el.select('.roo-money-amount-input', true).first();
49604     },
49605     
49606     currencyEl : function()
49607     {
49608         return this.el.select('.roo-money-currency-input', true).first();
49609     },
49610     
49611     hiddenEl : function()
49612     {
49613         return this.el.select('input.hidden-number-input',true).first();
49614     }
49615     
49616 });/**
49617  * @class Roo.bootstrap.BezierSignature
49618  * @extends Roo.bootstrap.Component
49619  * Bootstrap BezierSignature class
49620  * This script refer to:
49621  *    Title: Signature Pad
49622  *    Author: szimek
49623  *    Availability: https://github.com/szimek/signature_pad
49624  *
49625  * @constructor
49626  * Create a new BezierSignature
49627  * @param {Object} config The config object
49628  */
49629
49630 Roo.bootstrap.BezierSignature = function(config){
49631     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49632     this.addEvents({
49633         "resize" : true
49634     });
49635 };
49636
49637 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49638 {
49639      
49640     curve_data: [],
49641     
49642     is_empty: true,
49643     
49644     mouse_btn_down: true,
49645     
49646     /**
49647      * @cfg {int} canvas height
49648      */
49649     canvas_height: '200px',
49650     
49651     /**
49652      * @cfg {float|function} Radius of a single dot.
49653      */ 
49654     dot_size: false,
49655     
49656     /**
49657      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49658      */
49659     min_width: 0.5,
49660     
49661     /**
49662      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49663      */
49664     max_width: 2.5,
49665     
49666     /**
49667      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49668      */
49669     throttle: 16,
49670     
49671     /**
49672      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49673      */
49674     min_distance: 5,
49675     
49676     /**
49677      * @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.
49678      */
49679     bg_color: 'rgba(0, 0, 0, 0)',
49680     
49681     /**
49682      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49683      */
49684     dot_color: 'black',
49685     
49686     /**
49687      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49688      */ 
49689     velocity_filter_weight: 0.7,
49690     
49691     /**
49692      * @cfg {function} Callback when stroke begin. 
49693      */
49694     onBegin: false,
49695     
49696     /**
49697      * @cfg {function} Callback when stroke end.
49698      */
49699     onEnd: false,
49700     
49701     getAutoCreate : function()
49702     {
49703         var cls = 'roo-signature column';
49704         
49705         if(this.cls){
49706             cls += ' ' + this.cls;
49707         }
49708         
49709         var col_sizes = [
49710             'lg',
49711             'md',
49712             'sm',
49713             'xs'
49714         ];
49715         
49716         for(var i = 0; i < col_sizes.length; i++) {
49717             if(this[col_sizes[i]]) {
49718                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49719             }
49720         }
49721         
49722         var cfg = {
49723             tag: 'div',
49724             cls: cls,
49725             cn: [
49726                 {
49727                     tag: 'div',
49728                     cls: 'roo-signature-body',
49729                     cn: [
49730                         {
49731                             tag: 'canvas',
49732                             cls: 'roo-signature-body-canvas',
49733                             height: this.canvas_height,
49734                             width: this.canvas_width
49735                         }
49736                     ]
49737                 },
49738                 {
49739                     tag: 'input',
49740                     type: 'file',
49741                     style: 'display: none'
49742                 }
49743             ]
49744         };
49745         
49746         return cfg;
49747     },
49748     
49749     initEvents: function() 
49750     {
49751         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49752         
49753         var canvas = this.canvasEl();
49754         
49755         // mouse && touch event swapping...
49756         canvas.dom.style.touchAction = 'none';
49757         canvas.dom.style.msTouchAction = 'none';
49758         
49759         this.mouse_btn_down = false;
49760         canvas.on('mousedown', this._handleMouseDown, this);
49761         canvas.on('mousemove', this._handleMouseMove, this);
49762         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49763         
49764         if (window.PointerEvent) {
49765             canvas.on('pointerdown', this._handleMouseDown, this);
49766             canvas.on('pointermove', this._handleMouseMove, this);
49767             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49768         }
49769         
49770         if ('ontouchstart' in window) {
49771             canvas.on('touchstart', this._handleTouchStart, this);
49772             canvas.on('touchmove', this._handleTouchMove, this);
49773             canvas.on('touchend', this._handleTouchEnd, this);
49774         }
49775         
49776         Roo.EventManager.onWindowResize(this.resize, this, true);
49777         
49778         // file input event
49779         this.fileEl().on('change', this.uploadImage, this);
49780         
49781         this.clear();
49782         
49783         this.resize();
49784     },
49785     
49786     resize: function(){
49787         
49788         var canvas = this.canvasEl().dom;
49789         var ctx = this.canvasElCtx();
49790         var img_data = false;
49791         
49792         if(canvas.width > 0) {
49793             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49794         }
49795         // setting canvas width will clean img data
49796         canvas.width = 0;
49797         
49798         var style = window.getComputedStyle ? 
49799             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49800             
49801         var padding_left = parseInt(style.paddingLeft) || 0;
49802         var padding_right = parseInt(style.paddingRight) || 0;
49803         
49804         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49805         
49806         if(img_data) {
49807             ctx.putImageData(img_data, 0, 0);
49808         }
49809     },
49810     
49811     _handleMouseDown: function(e)
49812     {
49813         if (e.browserEvent.which === 1) {
49814             this.mouse_btn_down = true;
49815             this.strokeBegin(e);
49816         }
49817     },
49818     
49819     _handleMouseMove: function (e)
49820     {
49821         if (this.mouse_btn_down) {
49822             this.strokeMoveUpdate(e);
49823         }
49824     },
49825     
49826     _handleMouseUp: function (e)
49827     {
49828         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49829             this.mouse_btn_down = false;
49830             this.strokeEnd(e);
49831         }
49832     },
49833     
49834     _handleTouchStart: function (e) {
49835         
49836         e.preventDefault();
49837         if (e.browserEvent.targetTouches.length === 1) {
49838             // var touch = e.browserEvent.changedTouches[0];
49839             // this.strokeBegin(touch);
49840             
49841              this.strokeBegin(e); // assume e catching the correct xy...
49842         }
49843     },
49844     
49845     _handleTouchMove: function (e) {
49846         e.preventDefault();
49847         // var touch = event.targetTouches[0];
49848         // _this._strokeMoveUpdate(touch);
49849         this.strokeMoveUpdate(e);
49850     },
49851     
49852     _handleTouchEnd: function (e) {
49853         var wasCanvasTouched = e.target === this.canvasEl().dom;
49854         if (wasCanvasTouched) {
49855             e.preventDefault();
49856             // var touch = event.changedTouches[0];
49857             // _this._strokeEnd(touch);
49858             this.strokeEnd(e);
49859         }
49860     },
49861     
49862     reset: function () {
49863         this._lastPoints = [];
49864         this._lastVelocity = 0;
49865         this._lastWidth = (this.min_width + this.max_width) / 2;
49866         this.canvasElCtx().fillStyle = this.dot_color;
49867     },
49868     
49869     strokeMoveUpdate: function(e)
49870     {
49871         this.strokeUpdate(e);
49872         
49873         if (this.throttle) {
49874             this.throttleStroke(this.strokeUpdate, this.throttle);
49875         }
49876         else {
49877             this.strokeUpdate(e);
49878         }
49879     },
49880     
49881     strokeBegin: function(e)
49882     {
49883         var newPointGroup = {
49884             color: this.dot_color,
49885             points: []
49886         };
49887         
49888         if (typeof this.onBegin === 'function') {
49889             this.onBegin(e);
49890         }
49891         
49892         this.curve_data.push(newPointGroup);
49893         this.reset();
49894         this.strokeUpdate(e);
49895     },
49896     
49897     strokeUpdate: function(e)
49898     {
49899         var rect = this.canvasEl().dom.getBoundingClientRect();
49900         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49901         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49902         var lastPoints = lastPointGroup.points;
49903         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49904         var isLastPointTooClose = lastPoint
49905             ? point.distanceTo(lastPoint) <= this.min_distance
49906             : false;
49907         var color = lastPointGroup.color;
49908         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49909             var curve = this.addPoint(point);
49910             if (!lastPoint) {
49911                 this.drawDot({color: color, point: point});
49912             }
49913             else if (curve) {
49914                 this.drawCurve({color: color, curve: curve});
49915             }
49916             lastPoints.push({
49917                 time: point.time,
49918                 x: point.x,
49919                 y: point.y
49920             });
49921         }
49922     },
49923     
49924     strokeEnd: function(e)
49925     {
49926         this.strokeUpdate(e);
49927         if (typeof this.onEnd === 'function') {
49928             this.onEnd(e);
49929         }
49930     },
49931     
49932     addPoint:  function (point) {
49933         var _lastPoints = this._lastPoints;
49934         _lastPoints.push(point);
49935         if (_lastPoints.length > 2) {
49936             if (_lastPoints.length === 3) {
49937                 _lastPoints.unshift(_lastPoints[0]);
49938             }
49939             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49940             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49941             _lastPoints.shift();
49942             return curve;
49943         }
49944         return null;
49945     },
49946     
49947     calculateCurveWidths: function (startPoint, endPoint) {
49948         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49949             (1 - this.velocity_filter_weight) * this._lastVelocity;
49950
49951         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49952         var widths = {
49953             end: newWidth,
49954             start: this._lastWidth
49955         };
49956         
49957         this._lastVelocity = velocity;
49958         this._lastWidth = newWidth;
49959         return widths;
49960     },
49961     
49962     drawDot: function (_a) {
49963         var color = _a.color, point = _a.point;
49964         var ctx = this.canvasElCtx();
49965         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49966         ctx.beginPath();
49967         this.drawCurveSegment(point.x, point.y, width);
49968         ctx.closePath();
49969         ctx.fillStyle = color;
49970         ctx.fill();
49971     },
49972     
49973     drawCurve: function (_a) {
49974         var color = _a.color, curve = _a.curve;
49975         var ctx = this.canvasElCtx();
49976         var widthDelta = curve.endWidth - curve.startWidth;
49977         var drawSteps = Math.floor(curve.length()) * 2;
49978         ctx.beginPath();
49979         ctx.fillStyle = color;
49980         for (var i = 0; i < drawSteps; i += 1) {
49981         var t = i / drawSteps;
49982         var tt = t * t;
49983         var ttt = tt * t;
49984         var u = 1 - t;
49985         var uu = u * u;
49986         var uuu = uu * u;
49987         var x = uuu * curve.startPoint.x;
49988         x += 3 * uu * t * curve.control1.x;
49989         x += 3 * u * tt * curve.control2.x;
49990         x += ttt * curve.endPoint.x;
49991         var y = uuu * curve.startPoint.y;
49992         y += 3 * uu * t * curve.control1.y;
49993         y += 3 * u * tt * curve.control2.y;
49994         y += ttt * curve.endPoint.y;
49995         var width = curve.startWidth + ttt * widthDelta;
49996         this.drawCurveSegment(x, y, width);
49997         }
49998         ctx.closePath();
49999         ctx.fill();
50000     },
50001     
50002     drawCurveSegment: function (x, y, width) {
50003         var ctx = this.canvasElCtx();
50004         ctx.moveTo(x, y);
50005         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50006         this.is_empty = false;
50007     },
50008     
50009     clear: function()
50010     {
50011         var ctx = this.canvasElCtx();
50012         var canvas = this.canvasEl().dom;
50013         ctx.fillStyle = this.bg_color;
50014         ctx.clearRect(0, 0, canvas.width, canvas.height);
50015         ctx.fillRect(0, 0, canvas.width, canvas.height);
50016         this.curve_data = [];
50017         this.reset();
50018         this.is_empty = true;
50019     },
50020     
50021     fileEl: function()
50022     {
50023         return  this.el.select('input',true).first();
50024     },
50025     
50026     canvasEl: function()
50027     {
50028         return this.el.select('canvas',true).first();
50029     },
50030     
50031     canvasElCtx: function()
50032     {
50033         return this.el.select('canvas',true).first().dom.getContext('2d');
50034     },
50035     
50036     getImage: function(type)
50037     {
50038         if(this.is_empty) {
50039             return false;
50040         }
50041         
50042         // encryption ?
50043         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50044     },
50045     
50046     drawFromImage: function(img_src)
50047     {
50048         var img = new Image();
50049         
50050         img.onload = function(){
50051             this.canvasElCtx().drawImage(img, 0, 0);
50052         }.bind(this);
50053         
50054         img.src = img_src;
50055         
50056         this.is_empty = false;
50057     },
50058     
50059     selectImage: function()
50060     {
50061         this.fileEl().dom.click();
50062     },
50063     
50064     uploadImage: function(e)
50065     {
50066         var reader = new FileReader();
50067         
50068         reader.onload = function(e){
50069             var img = new Image();
50070             img.onload = function(){
50071                 this.reset();
50072                 this.canvasElCtx().drawImage(img, 0, 0);
50073             }.bind(this);
50074             img.src = e.target.result;
50075         }.bind(this);
50076         
50077         reader.readAsDataURL(e.target.files[0]);
50078     },
50079     
50080     // Bezier Point Constructor
50081     Point: (function () {
50082         function Point(x, y, time) {
50083             this.x = x;
50084             this.y = y;
50085             this.time = time || Date.now();
50086         }
50087         Point.prototype.distanceTo = function (start) {
50088             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50089         };
50090         Point.prototype.equals = function (other) {
50091             return this.x === other.x && this.y === other.y && this.time === other.time;
50092         };
50093         Point.prototype.velocityFrom = function (start) {
50094             return this.time !== start.time
50095             ? this.distanceTo(start) / (this.time - start.time)
50096             : 0;
50097         };
50098         return Point;
50099     }()),
50100     
50101     
50102     // Bezier Constructor
50103     Bezier: (function () {
50104         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50105             this.startPoint = startPoint;
50106             this.control2 = control2;
50107             this.control1 = control1;
50108             this.endPoint = endPoint;
50109             this.startWidth = startWidth;
50110             this.endWidth = endWidth;
50111         }
50112         Bezier.fromPoints = function (points, widths, scope) {
50113             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50114             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50115             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50116         };
50117         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50118             var dx1 = s1.x - s2.x;
50119             var dy1 = s1.y - s2.y;
50120             var dx2 = s2.x - s3.x;
50121             var dy2 = s2.y - s3.y;
50122             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50123             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50124             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50125             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50126             var dxm = m1.x - m2.x;
50127             var dym = m1.y - m2.y;
50128             var k = l2 / (l1 + l2);
50129             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50130             var tx = s2.x - cm.x;
50131             var ty = s2.y - cm.y;
50132             return {
50133                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50134                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50135             };
50136         };
50137         Bezier.prototype.length = function () {
50138             var steps = 10;
50139             var length = 0;
50140             var px;
50141             var py;
50142             for (var i = 0; i <= steps; i += 1) {
50143                 var t = i / steps;
50144                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50145                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50146                 if (i > 0) {
50147                     var xdiff = cx - px;
50148                     var ydiff = cy - py;
50149                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50150                 }
50151                 px = cx;
50152                 py = cy;
50153             }
50154             return length;
50155         };
50156         Bezier.prototype.point = function (t, start, c1, c2, end) {
50157             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50158             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50159             + (3.0 * c2 * (1.0 - t) * t * t)
50160             + (end * t * t * t);
50161         };
50162         return Bezier;
50163     }()),
50164     
50165     throttleStroke: function(fn, wait) {
50166       if (wait === void 0) { wait = 250; }
50167       var previous = 0;
50168       var timeout = null;
50169       var result;
50170       var storedContext;
50171       var storedArgs;
50172       var later = function () {
50173           previous = Date.now();
50174           timeout = null;
50175           result = fn.apply(storedContext, storedArgs);
50176           if (!timeout) {
50177               storedContext = null;
50178               storedArgs = [];
50179           }
50180       };
50181       return function wrapper() {
50182           var args = [];
50183           for (var _i = 0; _i < arguments.length; _i++) {
50184               args[_i] = arguments[_i];
50185           }
50186           var now = Date.now();
50187           var remaining = wait - (now - previous);
50188           storedContext = this;
50189           storedArgs = args;
50190           if (remaining <= 0 || remaining > wait) {
50191               if (timeout) {
50192                   clearTimeout(timeout);
50193                   timeout = null;
50194               }
50195               previous = now;
50196               result = fn.apply(storedContext, storedArgs);
50197               if (!timeout) {
50198                   storedContext = null;
50199                   storedArgs = [];
50200               }
50201           }
50202           else if (!timeout) {
50203               timeout = window.setTimeout(later, remaining);
50204           }
50205           return result;
50206       };
50207   }
50208   
50209 });
50210
50211  
50212
50213  // old names for form elements
50214 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50215 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50216 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50217 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50218 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50219 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50220 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50221 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50222 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50223 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50224 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50225 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50226 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50227 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50228 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50229 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50230 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50231 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50232 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50233 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50234 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50235 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50236 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50237 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50238 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50239 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50240
50241 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50242 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50243
50244 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50245 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50246
50247 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50248 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50249 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50250 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50251